Cloudwatch: Add template variable query function for listing log groups (#50100)

* cloud-datasources mob! :shipit:

* cloud-datasources mob! :shipit:

lastFile:public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.tsx

* mob next [ci-skip] [ci skip] [skip ci]

lastFile:public/app/plugins/datasource/cloudwatch/variables.ts

* cloud-datasources mob! 👶

lastFile:public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.test.tsx

* cloud-datasources mob! 👶

lastFile:public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.test.tsx

* mob next [ci-skip] [ci skip] [skip ci]

lastFile:public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.test.tsx

* cloud-datasources mob! :shipit:

lastFile:public/app/plugins/datasource/cloudwatch/variables.test.ts

* cloud-datasources mob! ☕

* prettier md

Co-authored-by: Kevin Yu <kevinwcyu@users.noreply.github.com>
Co-authored-by: Andres <andres.martinez@grafana.com>
Co-authored-by: Erik Sundell <erik.sundell87@gmail.com>
Co-authored-by: Adam Simpson <adam@adamsimpson.net>
This commit is contained in:
Yaelle Chaudy
2022-06-03 09:26:57 +02:00
committed by GitHub
co-authored by Kevin Yu Andres Erik Sundell Adam Simpson
parent 3c3039f5b3
commit bcf8320e07
7 changed files with 59 additions and 10 deletions
@@ -30,6 +30,7 @@ Read more about the available dimensions in the [CloudWatch Metrics and Dimensio
| `EC2 Instance Attributes` | Returns a list of attributes matching the specified `region`, `attribute_name`, and `filters`. | | `EC2 Instance Attributes` | Returns a list of attributes matching the specified `region`, `attribute_name`, and `filters`. |
| `Resource ARNs` | Returns a list of ARNs matching the specified `region`, `resource_type` and `tags`. | | `Resource ARNs` | Returns a list of ARNs matching the specified `region`, `resource_type` and `tags`. |
| `Statistics` | Returns a list of all the standard statistics. | | `Statistics` | Returns a list of all the standard statistics. |
| `LogGroups` | Returns a list of all log groups matching the specified `region`. |
For details about the metrics CloudWatch provides, please refer to the [CloudWatch documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html). For details about the metrics CloudWatch provides, please refer to the [CloudWatch documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html).
@@ -81,7 +81,7 @@ describe('VariableEditor', () => {
render(<VariableQueryEditor {...props} />); render(<VariableQueryEditor {...props} />);
await waitFor(() => { await waitFor(() => {
const querySelect = screen.queryByRole('combobox', { name: 'Query Type' }); const querySelect = screen.queryByRole('combobox', { name: 'Query type' });
expect(querySelect).toBeInTheDocument(); expect(querySelect).toBeInTheDocument();
expect(screen.queryByText('Regions')).toBeInTheDocument(); expect(screen.queryByText('Regions')).toBeInTheDocument();
// Should not render any fields besides Query Type // Should not render any fields besides Query Type
@@ -103,7 +103,7 @@ describe('VariableEditor', () => {
render(<VariableQueryEditor {...props} />); render(<VariableQueryEditor {...props} />);
await waitFor(() => { await waitFor(() => {
const querySelect = screen.queryByRole('combobox', { name: 'Query Type' }); const querySelect = screen.queryByRole('combobox', { name: 'Query type' });
expect(querySelect).toBeInTheDocument(); expect(querySelect).toBeInTheDocument();
expect(screen.queryByText('Metrics')).toBeInTheDocument(); expect(screen.queryByText('Metrics')).toBeInTheDocument();
const regionSelect = screen.queryByRole('combobox', { name: 'Region' }); const regionSelect = screen.queryByRole('combobox', { name: 'Region' });
@@ -216,7 +216,7 @@ describe('VariableEditor', () => {
}; };
render(<VariableQueryEditor {...props} />); render(<VariableQueryEditor {...props} />);
const querySelect = screen.queryByLabelText('Query Type'); const querySelect = screen.queryByLabelText('Query type');
expect(querySelect).toBeInTheDocument(); expect(querySelect).toBeInTheDocument();
expect(screen.queryByText('Dimension Values')).toBeInTheDocument(); expect(screen.queryByText('Dimension Values')).toBeInTheDocument();
const regionSelect = screen.getByRole('combobox', { name: 'Region' }); const regionSelect = screen.getByRole('combobox', { name: 'Region' });
@@ -240,4 +240,21 @@ describe('VariableEditor', () => {
}); });
}); });
}); });
describe('LogGroups queryType is selected', () => {
it('should only render region and prefix', async () => {
const props = defaultProps;
props.query = {
...defaultQuery,
queryType: VariableQueryType.LogGroups,
};
render(<VariableQueryEditor {...props} />);
await waitFor(() => {
screen.getByLabelText('Log group prefix');
screen.getByLabelText('Region');
});
expect(screen.queryByLabelText('Namespace')).not.toBeInTheDocument();
});
});
}); });
@@ -25,6 +25,7 @@ const queryTypes: Array<{ value: string; label: string }> = [
{ value: VariableQueryType.EC2InstanceAttributes, label: 'EC2 Instance Attributes' }, { value: VariableQueryType.EC2InstanceAttributes, label: 'EC2 Instance Attributes' },
{ value: VariableQueryType.ResourceArns, label: 'Resource ARNs' }, { value: VariableQueryType.ResourceArns, label: 'Resource ARNs' },
{ value: VariableQueryType.Statistics, label: 'Statistics' }, { value: VariableQueryType.Statistics, label: 'Statistics' },
{ value: VariableQueryType.LogGroups, label: 'Log Groups' },
]; ];
export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => { export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => {
@@ -88,6 +89,7 @@ export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => {
VariableQueryType.EBSVolumeIDs, VariableQueryType.EBSVolumeIDs,
VariableQueryType.EC2InstanceAttributes, VariableQueryType.EC2InstanceAttributes,
VariableQueryType.ResourceArns, VariableQueryType.ResourceArns,
VariableQueryType.LogGroups,
].includes(parsedQuery.queryType); ].includes(parsedQuery.queryType);
const hasNamespaceField = [ const hasNamespaceField = [
VariableQueryType.Metrics, VariableQueryType.Metrics,
@@ -100,7 +102,7 @@ export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => {
value={parsedQuery.queryType} value={parsedQuery.queryType}
options={queryTypes} options={queryTypes}
onChange={(value: VariableQueryType) => onQueryChange({ ...parsedQuery, queryType: value })} onChange={(value: VariableQueryType) => onQueryChange({ ...parsedQuery, queryType: value })}
label="Query Type" label="Query type"
inputId={`variable-query-type-${query.refId}`} inputId={`variable-query-type-${query.refId}`}
/> />
{hasRegionField && ( {hasRegionField && (
@@ -135,7 +137,7 @@ export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => {
value={dimensionKey || null} value={dimensionKey || null}
options={dimensionKeys} options={dimensionKeys}
onChange={(value: string) => onQueryChange({ ...parsedQuery, dimensionKey: value })} onChange={(value: string) => onQueryChange({ ...parsedQuery, dimensionKey: value })}
label="Dimension Key" label="Dimension key"
inputId={`variable-query-dimension-key-${query.refId}`} inputId={`variable-query-dimension-key-${query.refId}`}
/> />
<InlineField label="Dimensions" labelWidth={20} tooltip="Dimensions to filter the returned values on"> <InlineField label="Dimensions" labelWidth={20} tooltip="Dimensions to filter the returned values on">
@@ -163,9 +165,8 @@ export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => {
<> <>
<VariableTextField <VariableTextField
value={parsedQuery.attributeName} value={parsedQuery.attributeName}
placeholder="attribute name"
onBlur={(value: string) => onQueryChange({ ...parsedQuery, attributeName: value })} onBlur={(value: string) => onQueryChange({ ...parsedQuery, attributeName: value })}
label="Attribute Name" label="Attribute name"
interactive={true} interactive={true}
tooltip={ tooltip={
<> <>
@@ -210,9 +211,8 @@ export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => {
<> <>
<VariableTextField <VariableTextField
value={parsedQuery.resourceType} value={parsedQuery.resourceType}
placeholder="resource type"
onBlur={(value: string) => onQueryChange({ ...parsedQuery, resourceType: value })} onBlur={(value: string) => onQueryChange({ ...parsedQuery, resourceType: value })}
label="Resource Type" label="Resource type"
/> />
<InlineField label="Tags" labelWidth={20} tooltip="Tags to filter the returned values on."> <InlineField label="Tags" labelWidth={20} tooltip="Tags to filter the returned values on.">
<MultiFilter <MultiFilter
@@ -225,6 +225,13 @@ export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => {
</InlineField> </InlineField>
</> </>
)} )}
{parsedQuery.queryType === VariableQueryType.LogGroups && (
<VariableTextField
value={query.logGroupPrefix ?? ''}
onBlur={(value: string) => onQueryChange({ ...parsedQuery, logGroupPrefix: value })}
label="Log group prefix"
/>
)}
</> </>
); );
}; };
@@ -6,9 +6,9 @@ const LABEL_WIDTH = 20;
interface VariableTextFieldProps { interface VariableTextFieldProps {
onBlur: (value: string) => void; onBlur: (value: string) => void;
placeholder: string;
value: string; value: string;
label: string; label: string;
placeholder?: string;
tooltip?: PopoverContent; tooltip?: PopoverContent;
interactive?: boolean; interactive?: boolean;
} }
@@ -376,6 +376,7 @@ export enum VariableQueryType {
EC2InstanceAttributes = 'ec2InstanceAttributes', EC2InstanceAttributes = 'ec2InstanceAttributes',
ResourceArns = 'resourceARNs', ResourceArns = 'resourceARNs',
Statistics = 'statistics', Statistics = 'statistics',
LogGroups = 'logGroups',
} }
export interface OldVariableQuery extends DataQuery { export interface OldVariableQuery extends DataQuery {
@@ -404,6 +405,7 @@ export interface VariableQuery extends DataQuery {
attributeName: string; attributeName: string;
resourceType: string; resourceType: string;
tags?: MultiFilters; tags?: MultiFilters;
logGroupPrefix?: string;
} }
export interface LegacyAnnotationQuery extends MetricStat, DataQuery { export interface LegacyAnnotationQuery extends MetricStat, DataQuery {
@@ -19,6 +19,7 @@ ds.datasource.getRegions = jest.fn().mockResolvedValue([{ label: 'a', value: 'a'
ds.datasource.getNamespaces = jest.fn().mockResolvedValue([{ label: 'b', value: 'b' }]); ds.datasource.getNamespaces = jest.fn().mockResolvedValue([{ label: 'b', value: 'b' }]);
ds.datasource.getMetrics = jest.fn().mockResolvedValue([{ label: 'c', value: 'c' }]); ds.datasource.getMetrics = jest.fn().mockResolvedValue([{ label: 'c', value: 'c' }]);
ds.datasource.getDimensionKeys = jest.fn().mockResolvedValue([{ label: 'd', value: 'd' }]); ds.datasource.getDimensionKeys = jest.fn().mockResolvedValue([{ label: 'd', value: 'd' }]);
ds.datasource.describeLogGroups = jest.fn().mockResolvedValue(['a', 'b']);
const getDimensionValues = jest.fn().mockResolvedValue([{ label: 'e', value: 'e' }]); const getDimensionValues = jest.fn().mockResolvedValue([{ label: 'e', value: 'e' }]);
const getEbsVolumeIds = jest.fn().mockResolvedValue([{ label: 'f', value: 'f' }]); const getEbsVolumeIds = jest.fn().mockResolvedValue([{ label: 'f', value: 'f' }]);
const getEc2InstanceAttribute = jest.fn().mockResolvedValue([{ label: 'g', value: 'g' }]); const getEc2InstanceAttribute = jest.fn().mockResolvedValue([{ label: 'g', value: 'g' }]);
@@ -167,4 +168,14 @@ describe('variables', () => {
{ text: 'SampleCount', value: 'SampleCount', expandable: true }, { text: 'SampleCount', value: 'SampleCount', expandable: true },
]); ]);
}); });
describe('log groups', () => {
it('should call describe log groups', async () => {
const result = await variables.execute({ ...defaultQuery, queryType: VariableQueryType.LogGroups });
expect(result).toEqual([
{ text: 'a', value: 'a', expandable: true },
{ text: 'b', value: 'b', expandable: true },
]);
});
});
}); });
@@ -45,6 +45,8 @@ export class CloudWatchVariableSupport extends CustomVariableSupport<CloudWatchD
return this.handleResourceARNsQuery(query); return this.handleResourceARNsQuery(query);
case VariableQueryType.Statistics: case VariableQueryType.Statistics:
return this.handleStatisticsQuery(); return this.handleStatisticsQuery();
case VariableQueryType.LogGroups:
return this.handleLogGroupsQuery(query);
} }
} catch (error) { } catch (error) {
console.error(`Could not run CloudWatchMetricFindQuery ${query}`, error); console.error(`Could not run CloudWatchMetricFindQuery ${query}`, error);
@@ -52,6 +54,15 @@ export class CloudWatchVariableSupport extends CustomVariableSupport<CloudWatchD
} }
} }
async handleLogGroupsQuery({ region, logGroupPrefix }: VariableQuery) {
const logGroups = await this.datasource.describeLogGroups({ region, logGroupNamePrefix: logGroupPrefix });
return logGroups.map((s) => ({
text: s,
value: s,
expandable: true,
}));
}
async handleRegionsQuery() { async handleRegionsQuery() {
const regions = await this.datasource.getRegions(); const regions = await this.datasource.getRegions();
return regions.map((s: { label: string; value: string }) => ({ return regions.map((s: { label: string; value: string }) => ({