Skip to content

Commit

Permalink
[Rule Registry] ignore some errors while updating index mappings (ela…
Browse files Browse the repository at this point in the history
…stic#140778)

resolves elastic#139969

Changes the ResourceInstaller to ignore cases when the elasticsearch
simulateIndexTemplate() API returns an error or empty mappings, logging an
error instead. This will hopefully allow initialization to continue to set
up the alerts-as-data indices and backing resources for future indexing.

Also adds _meta: { managed: true } to the ILM policy, which should show a
warning in Kibana UX when attempting to make changes to the policy. Which
was the cause of why simulateIndexTemplate() could return empty mappings.

(cherry picked from commit 01daf31)
  • Loading branch information
pmuellr committed Sep 20, 2022
1 parent ba0afde commit 2a7e0a3
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

export const defaultLifecyclePolicy = {
policy: {
_meta: {
managed: true,
},
phases: {
hot: {
actions: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { AlertConsumers } from '@kbn/rule-data-utils';

import { Dataset } from './index_options';
import { IndexInfo } from './index_info';
import { elasticsearchServiceMock } from '@kbn/core/server/mocks';
import { elasticsearchServiceMock, ElasticsearchClientMock } from '@kbn/core/server/mocks';
import {
DEFAULT_ILM_POLICY_ID,
ECS_COMPONENT_TEMPLATE_NAME,
Expand Down Expand Up @@ -139,4 +139,145 @@ describe('resourceInstaller', () => {
);
});
});

// These tests only test the updateAliasWriteIndexMapping()
// method of ResourceInstaller, however to test that, you
// have to call installAndUpdateNamespaceLevelResources().
// So there's a bit of setup. But the only real difference
// with the tests is what the es client simulateIndexTemplate()
// mock returns, as set in the test.
describe('updateAliasWriteIndexMapping()', () => {
const SimulateTemplateResponse = {
template: {
aliases: {
alias_name_1: {
is_hidden: true,
},
alias_name_2: {
is_hidden: true,
},
},
mappings: { enabled: false },
settings: {},
},
};

const GetAliasResponse = {
real_index: {
aliases: {
alias_1: {
is_hidden: true,
},
alias_2: {
is_hidden: true,
},
},
},
};

function setup(mockClusterClient: ElasticsearchClientMock) {
mockClusterClient.indices.simulateTemplate.mockImplementation(
async () => SimulateTemplateResponse
);
mockClusterClient.indices.getAlias.mockImplementation(async () => GetAliasResponse);

const logger = loggerMock.create();
const resourceInstallerParams = {
logger,
isWriteEnabled: true,
disabledRegistrationContexts: [],
getResourceName: jest.fn(),
getClusterClient: async () => mockClusterClient,
pluginStop$,
};
const indexOptions = {
feature: AlertConsumers.OBSERVABILITY,
registrationContext: 'observability.metrics',
dataset: Dataset.alerts,
componentTemplateRefs: [],
componentTemplates: [
{
name: 'mappings',
},
],
};

const installer = new ResourceInstaller(resourceInstallerParams);
const indexInfo = new IndexInfo({ indexOptions, kibanaVersion: '8.4.0' });

return { installer, indexInfo, logger };
}

it('succeeds on the happy path', async () => {
const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient();
mockClusterClient.indices.simulateIndexTemplate.mockImplementation(
async () => SimulateTemplateResponse
);

const { installer, indexInfo } = setup(mockClusterClient);

let error: string | undefined;
try {
await installer.installAndUpdateNamespaceLevelResources(indexInfo, 'default');
} catch (err) {
error = err.message;
}
expect(error).toBeFalsy();
});

it('gracefully fails on error simulating mappings', async () => {
const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient();
mockClusterClient.indices.simulateIndexTemplate.mockImplementation(async () => {
throw new Error('expecting simulateIndexTemplate() to throw');
});

const { installer, indexInfo, logger } = setup(mockClusterClient);

let error: string | undefined;
try {
await installer.installAndUpdateNamespaceLevelResources(indexInfo, 'default');
} catch (err) {
error = err.message;
}
expect(error).toBeFalsy();

const errorMessages = loggerMock.collect(logger).error;
expect(errorMessages).toMatchInlineSnapshot(`
Array [
Array [
"Ignored PUT mappings for alias alias_1; error generating simulated mappings: expecting simulateIndexTemplate() to throw",
],
Array [
"Ignored PUT mappings for alias alias_2; error generating simulated mappings: expecting simulateIndexTemplate() to throw",
],
]
`);
});

it('gracefully fails on empty mappings', async () => {
const mockClusterClient = elasticsearchServiceMock.createElasticsearchClient();
mockClusterClient.indices.simulateIndexTemplate.mockImplementation(async () => ({}));

const { installer, indexInfo, logger } = setup(mockClusterClient);

let error: string | undefined;
try {
await installer.installAndUpdateNamespaceLevelResources(indexInfo, 'default');
} catch (err) {
error = err.message;
}
expect(error).toBeFalsy();
const errorMessages = loggerMock.collect(logger).error;
expect(errorMessages).toMatchInlineSnapshot(`
Array [
Array [
"Ignored PUT mappings for alias alias_1; simulated mappings were empty",
],
Array [
"Ignored PUT mappings for alias alias_2; simulated mappings were empty",
],
]
`);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,26 @@ export class ResourceInstaller {
private async updateAliasWriteIndexMapping({ index, alias }: ConcreteIndexInfo) {
const { logger, getClusterClient } = this.options;
const clusterClient = await getClusterClient();
const simulatedIndexMapping = await clusterClient.indices.simulateIndexTemplate({
name: index,
});

let simulatedIndexMapping: estypes.IndicesSimulateIndexTemplateResponse;
try {
simulatedIndexMapping = await clusterClient.indices.simulateIndexTemplate({
name: index,
});
} catch (err) {
logger.error(
`Ignored PUT mappings for alias ${alias}; error generating simulated mappings: ${err.message}`
);
return;
}

const simulatedMapping = get(simulatedIndexMapping, ['template', 'mappings']);

if (simulatedMapping == null) {
logger.error(`Ignored PUT mappings for alias ${alias}; simulated mappings were empty`);
return;
}

try {
await clusterClient.indices.putMapping({
index,
Expand Down

0 comments on commit 2a7e0a3

Please sign in to comment.