mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Response Ops][Alerting] Adding null checks when iterating through index template list (#158742)
## Summary When updating common component templates during AAD resource installation, we occassionally run into errors where the index templates using the common component template has a total field limit that is less than the new total number of fields with the updated component template. When this occurs, we query the ES index template API to get the list of index templates in order to check their `composed_of` field to see if they reference the specific component template and act accordingly. In theory, `composed_of` is (and is typed as) a required array. In practice, it seems that this field can be missing from the response. Since we're doing a `.includes` check on this array, this can lead to null dereference errors that can halt alerts as data resource installation. This PR adds a check to make sure the `composed_of` field exists before using it. ## To Verify 1. Run Kibana 8.6 locally and create a metric threshold rule & detection rule that generates alerts. Make sure the alert index templates for these rule types have been created and the rule runs successfully. 2. Update to Kibana 8.7. Make sure the rules run successfully and generate alerts. Create a data view with no component templates. To do this, go to `Stack Management > Index Management > Index Templates` and click `Create template`. Fill in a name and index pattern (`test*` is fine) and make sure the `Create data stream` switch is checked. Click through all the remaining options without setting anything else. Use `GET /_index_template/<name>` to verify that this index template has no `composed_of` field 3. Update to this branch. All alert resources should be installed successfully and there should be no errors on startup.
This commit is contained in:
parent
36ae6c8e62
commit
0f02b9e968
2 changed files with 116 additions and 2 deletions
|
@ -189,6 +189,112 @@ describe('createOrUpdateComponentTemplate', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it(`should update index template field limit and retry if putTemplate throws error with field limit error when there are malformed index templates`, async () => {
|
||||
clusterClient.cluster.putComponentTemplate.mockRejectedValueOnce(
|
||||
new EsErrors.ResponseError(
|
||||
elasticsearchClientMock.createApiResponse({
|
||||
statusCode: 400,
|
||||
body: {
|
||||
error: {
|
||||
root_cause: [
|
||||
{
|
||||
type: 'illegal_argument_exception',
|
||||
reason:
|
||||
'updating component template [.alerts-ecs-mappings] results in invalid composable template [.alerts-security.alerts-default-index-template] after templates are merged',
|
||||
},
|
||||
],
|
||||
type: 'illegal_argument_exception',
|
||||
reason:
|
||||
'updating component template [.alerts-ecs-mappings] results in invalid composable template [.alerts-security.alerts-default-index-template] after templates are merged',
|
||||
caused_by: {
|
||||
type: 'illegal_argument_exception',
|
||||
reason:
|
||||
'composable template [.alerts-security.alerts-default-index-template] template after composition with component templates [.alerts-ecs-mappings, .alerts-security.alerts-mappings, .alerts-technical-mappings] is invalid',
|
||||
caused_by: {
|
||||
type: 'illegal_argument_exception',
|
||||
reason:
|
||||
'invalid composite mappings for [.alerts-security.alerts-default-index-template]',
|
||||
caused_by: {
|
||||
type: 'illegal_argument_exception',
|
||||
reason: 'Limit of total fields [1900] has been exceeded',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
const existingIndexTemplate = {
|
||||
name: 'test-template',
|
||||
index_template: {
|
||||
index_patterns: ['test*'],
|
||||
composed_of: ['test-mappings'],
|
||||
template: {
|
||||
settings: {
|
||||
auto_expand_replicas: '0-1',
|
||||
hidden: true,
|
||||
'index.lifecycle': {
|
||||
name: '.alerts-ilm-policy',
|
||||
rollover_alias: `.alerts-empty-default`,
|
||||
},
|
||||
'index.mapping.total_fields.limit': 1800,
|
||||
},
|
||||
mappings: {
|
||||
dynamic: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
clusterClient.indices.getIndexTemplate.mockResolvedValueOnce({
|
||||
index_templates: [
|
||||
existingIndexTemplate,
|
||||
{
|
||||
name: 'lyndon',
|
||||
// @ts-expect-error
|
||||
index_template: {
|
||||
index_patterns: ['intel*'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'sample_ds',
|
||||
// @ts-expect-error
|
||||
index_template: {
|
||||
index_patterns: ['sample_ds-*'],
|
||||
data_stream: {
|
||||
hidden: false,
|
||||
allow_custom_routing: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await createOrUpdateComponentTemplate({
|
||||
logger,
|
||||
esClient: clusterClient,
|
||||
template: ComponentTemplate,
|
||||
totalFieldsLimit: 2500,
|
||||
});
|
||||
|
||||
expect(clusterClient.cluster.putComponentTemplate).toHaveBeenCalledTimes(2);
|
||||
expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledTimes(1);
|
||||
expect(clusterClient.indices.putIndexTemplate).toHaveBeenCalledWith({
|
||||
name: existingIndexTemplate.name,
|
||||
body: {
|
||||
...existingIndexTemplate.index_template,
|
||||
template: {
|
||||
...existingIndexTemplate.index_template.template,
|
||||
settings: {
|
||||
...existingIndexTemplate.index_template.template?.settings,
|
||||
'index.mapping.total_fields.limit': 2500,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`should retry getIndexTemplate and putIndexTemplate on transient ES errors`, async () => {
|
||||
clusterClient.cluster.putComponentTemplate.mockRejectedValueOnce(
|
||||
new EsErrors.ResponseError(
|
||||
|
|
|
@ -32,8 +32,16 @@ const getIndexTemplatesUsingComponentTemplate = async (
|
|||
{ logger }
|
||||
);
|
||||
const indexTemplatesUsingComponentTemplate = (indexTemplates ?? []).filter(
|
||||
(indexTemplate: IndicesGetIndexTemplateIndexTemplateItem) =>
|
||||
indexTemplate.index_template.composed_of.includes(componentTemplateName)
|
||||
(indexTemplate: IndicesGetIndexTemplateIndexTemplateItem) => {
|
||||
if (
|
||||
indexTemplate &&
|
||||
indexTemplate.index_template &&
|
||||
indexTemplate.index_template.composed_of
|
||||
) {
|
||||
return indexTemplate.index_template.composed_of.includes(componentTemplateName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
await asyncForEach(
|
||||
indexTemplatesUsingComponentTemplate,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue