From b5abc4ec7e308815b0f338f4c836a9caf3ee48a3 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Fri, 13 Sep 2024 19:34:46 -0700 Subject: [PATCH] [UII] Make constant keyword backfill optional (#192921) ## Summary Follow up to #188145. In some edge cases (https://github.com/elastic/sdh-beats/issues/5156), users could override the index template used by integration data streams. It is possible to create an index template without mappings, this causes `fillConstantKeywordValues` to receive an undefined object when upgrading the integration, and the upgrade then fails. This PR makes the backfill operation here more fail-safe. --- .../elasticsearch/template/template.test.ts | 104 ++++++++++++++++++ .../epm/elasticsearch/template/template.ts | 14 ++- 2 files changed, 114 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts index 989c28e205052..371ea4a470eaf 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts @@ -1867,6 +1867,110 @@ describe('EPM template', () => { }, }); }); + + it('should fill constant keywords from previous mappings', async () => { + const esClient = elasticsearchServiceMock.createElasticsearchClient(); + esClient.indices.getDataStream.mockResponse({ + data_streams: [{ name: 'test-constant.keyword-default' }], + } as any); + esClient.indices.get.mockResponse({ + 'test-constant.keyword-default': { + mappings: { + properties: { + some_keyword_field: { + type: 'constant_keyword', + }, + }, + }, + }, + } as any); + esClient.indices.simulateTemplate.mockResponse({ + template: { + settings: { index: {} }, + mappings: { + properties: { + some_keyword_field: { + type: 'constant_keyword', + value: 'some_value', + }, + }, + }, + }, + } as any); + const logger = loggerMock.create(); + await updateCurrentWriteIndices(esClient, logger, [ + { + templateName: 'test', + indexTemplate: { + index_patterns: ['test-constant.keyword-*'], + template: { + template: { + settings: { index: {} }, + mappings: { properties: {} }, + }, + }, + } as any, + }, + ]); + const putMappingsCalls = esClient.indices.putMapping.mock.calls; + expect(putMappingsCalls).toHaveLength(1); + expect(putMappingsCalls[0][0]).toEqual({ + index: 'test-constant.keyword-default', + body: { + properties: { + some_keyword_field: { + type: 'constant_keyword', + value: 'some_value', + }, + }, + }, + write_index_only: true, + }); + }); + + it('should not error when previous mappings are not found', async () => { + const esClient = elasticsearchServiceMock.createElasticsearchClient(); + esClient.indices.getDataStream.mockResponse({ + data_streams: [{ name: 'test-constant.keyword-default' }], + } as any); + esClient.indices.get.mockResponse({ + 'test-constant.keyword-default': { + mappings: { + properties: { + some_keyword_field: { + type: 'constant_keyword', + }, + }, + }, + }, + } as any); + esClient.indices.simulateTemplate.mockResponse({ + template: {}, + } as any); + const logger = loggerMock.create(); + await updateCurrentWriteIndices(esClient, logger, [ + { + templateName: 'test', + indexTemplate: { + index_patterns: ['test-constant.keyword-*'], + template: { + template: { + settings: { index: {} }, + mappings: { properties: {} }, + }, + }, + } as any, + }, + ]); + const putMappingsCalls = esClient.indices.putMapping.mock.calls; + expect(putMappingsCalls).toHaveLength(1); + expect(putMappingsCalls[0][0]).toEqual({ + index: 'test-constant.keyword-default', + body: {}, + write_index_only: true, + }); + }); + it('should rollover on expected error', async () => { const esClient = elasticsearchServiceMock.createElasticsearchClient(); esClient.indices.getDataStream.mockResponse({ diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts index efd621e9cc63a..3709975c57a5e 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts @@ -1027,10 +1027,16 @@ const updateExistingDataStream = async ({ ); settings = simulateResult.template.settings; - mappings = fillConstantKeywordValues( - currentBackingIndexConfig?.mappings || {}, - simulateResult.template.mappings - ); + + try { + mappings = fillConstantKeywordValues( + currentBackingIndexConfig?.mappings || {}, + simulateResult.template.mappings || {} + ); + } catch (err) { + logger.error(`Error filling constant keyword values: ${err}`); + mappings = simulateResult.template.mappings; + } lifecycle = simulateResult.template.lifecycle;