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:
co-authored by
Kevin Yu
Andres
Erik Sundell
Adam Simpson
parent
3c3039f5b3
commit
bcf8320e07
@@ -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).
|
||||||
|
|
||||||
|
|||||||
+20
-3
@@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+13
-6
@@ -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"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -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 }) => ({
|
||||||
|
|||||||
Reference in New Issue
Block a user