Migrate to the composable session index template on the startup. (#121311) (#122028)

Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>
This commit is contained in:
Kibana Machine 2021-12-27 12:13:30 -05:00 committed by GitHub
parent 3fe3d672f9
commit 51a931f21e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 123 additions and 40 deletions

View file

@ -42,13 +42,19 @@ describe('Session index', () => {
expect(mockElasticsearchClient.indices.existsTemplate).toHaveBeenCalledWith({
name: indexTemplateName,
});
expect(mockElasticsearchClient.indices.existsIndexTemplate).toHaveBeenCalledWith({
name: indexTemplateName,
});
expect(mockElasticsearchClient.indices.exists).toHaveBeenCalledWith({
index: getSessionIndexTemplate(indexName).index_patterns[0],
index: getSessionIndexTemplate(indexTemplateName, indexName).index_patterns[0],
});
}
it('debounces initialize calls', async () => {
mockElasticsearchClient.indices.existsTemplate.mockResolvedValue(
securityMock.createApiResponse({ body: false })
);
mockElasticsearchClient.indices.existsIndexTemplate.mockResolvedValue(
securityMock.createApiResponse({ body: true })
);
mockElasticsearchClient.indices.exists.mockResolvedValue(
@ -65,8 +71,11 @@ describe('Session index', () => {
assertExistenceChecksPerformed();
});
it('creates neither index template nor index if they exist', async () => {
it('does not delete legacy index template if it does not exist and creates neither index template nor index if they exist', async () => {
mockElasticsearchClient.indices.existsTemplate.mockResolvedValue(
securityMock.createApiResponse({ body: false })
);
mockElasticsearchClient.indices.existsIndexTemplate.mockResolvedValue(
securityMock.createApiResponse({ body: true })
);
mockElasticsearchClient.indices.exists.mockResolvedValue(
@ -76,24 +85,57 @@ describe('Session index', () => {
await sessionIndex.initialize();
assertExistenceChecksPerformed();
expect(mockElasticsearchClient.indices.deleteTemplate).not.toHaveBeenCalled();
expect(mockElasticsearchClient.indices.putIndexTemplate).not.toHaveBeenCalled();
expect(mockElasticsearchClient.indices.create).not.toHaveBeenCalled();
});
it('deletes legacy index template if needed and creates both index template and index if they do not exist', async () => {
mockElasticsearchClient.indices.existsTemplate.mockResolvedValue(
securityMock.createApiResponse({ body: true })
);
mockElasticsearchClient.indices.existsIndexTemplate.mockResolvedValue(
securityMock.createApiResponse({ body: false })
);
mockElasticsearchClient.indices.exists.mockResolvedValue(
securityMock.createApiResponse({ body: false })
);
await sessionIndex.initialize();
const expectedIndexTemplate = getSessionIndexTemplate(indexTemplateName, indexName);
assertExistenceChecksPerformed();
expect(mockElasticsearchClient.indices.deleteTemplate).toHaveBeenCalledWith({
name: indexTemplateName,
});
expect(mockElasticsearchClient.indices.putIndexTemplate).toHaveBeenCalledWith(
expectedIndexTemplate
);
expect(mockElasticsearchClient.indices.create).toHaveBeenCalledWith({
index: expectedIndexTemplate.index_patterns[0],
});
});
it('creates both index template and index if they do not exist', async () => {
mockElasticsearchClient.indices.existsTemplate.mockResolvedValue(
securityMock.createApiResponse({ body: false })
);
mockElasticsearchClient.indices.existsIndexTemplate.mockResolvedValue(
securityMock.createApiResponse({ body: false })
);
mockElasticsearchClient.indices.exists.mockResolvedValue(
securityMock.createApiResponse({ body: false })
);
await sessionIndex.initialize();
const expectedIndexTemplate = getSessionIndexTemplate(indexName);
const expectedIndexTemplate = getSessionIndexTemplate(indexTemplateName, indexName);
assertExistenceChecksPerformed();
expect(mockElasticsearchClient.indices.putTemplate).toHaveBeenCalledWith({
name: indexTemplateName,
body: expectedIndexTemplate,
});
expect(mockElasticsearchClient.indices.deleteTemplate).not.toHaveBeenCalled();
expect(mockElasticsearchClient.indices.putIndexTemplate).toHaveBeenCalledWith(
expectedIndexTemplate
);
expect(mockElasticsearchClient.indices.create).toHaveBeenCalledWith({
index: expectedIndexTemplate.index_patterns[0],
});
@ -103,6 +145,9 @@ describe('Session index', () => {
mockElasticsearchClient.indices.existsTemplate.mockResolvedValue(
securityMock.createApiResponse({ body: false })
);
mockElasticsearchClient.indices.existsIndexTemplate.mockResolvedValue(
securityMock.createApiResponse({ body: false })
);
mockElasticsearchClient.indices.exists.mockResolvedValue(
securityMock.createApiResponse({ body: true })
);
@ -110,14 +155,17 @@ describe('Session index', () => {
await sessionIndex.initialize();
assertExistenceChecksPerformed();
expect(mockElasticsearchClient.indices.putTemplate).toHaveBeenCalledWith({
name: indexTemplateName,
body: getSessionIndexTemplate(indexName),
});
expect(mockElasticsearchClient.indices.deleteTemplate).not.toHaveBeenCalled();
expect(mockElasticsearchClient.indices.putIndexTemplate).toHaveBeenCalledWith(
getSessionIndexTemplate(indexTemplateName, indexName)
);
});
it('creates only index if it does not exist even if index template exists', async () => {
mockElasticsearchClient.indices.existsTemplate.mockResolvedValue(
securityMock.createApiResponse({ body: false })
);
mockElasticsearchClient.indices.existsIndexTemplate.mockResolvedValue(
securityMock.createApiResponse({ body: true })
);
mockElasticsearchClient.indices.exists.mockResolvedValue(
@ -127,13 +175,18 @@ describe('Session index', () => {
await sessionIndex.initialize();
assertExistenceChecksPerformed();
expect(mockElasticsearchClient.indices.deleteTemplate).not.toHaveBeenCalled();
expect(mockElasticsearchClient.indices.putIndexTemplate).not.toHaveBeenCalled();
expect(mockElasticsearchClient.indices.create).toHaveBeenCalledWith({
index: getSessionIndexTemplate(indexName).index_patterns[0],
index: getSessionIndexTemplate(indexTemplateName, indexName).index_patterns[0],
});
});
it('does not fail if tries to create index when it exists already', async () => {
mockElasticsearchClient.indices.existsTemplate.mockResolvedValue(
securityMock.createApiResponse({ body: false })
);
mockElasticsearchClient.indices.existsIndexTemplate.mockResolvedValue(
securityMock.createApiResponse({ body: true })
);
mockElasticsearchClient.indices.exists.mockResolvedValue(
@ -154,8 +207,8 @@ describe('Session index', () => {
const unexpectedError = new errors.ResponseError(
securityMock.createApiResponse(securityMock.createApiResponse({ body: { type: 'Uh oh.' } }))
);
mockElasticsearchClient.indices.existsTemplate.mockRejectedValueOnce(unexpectedError);
mockElasticsearchClient.indices.existsTemplate.mockResolvedValueOnce(
mockElasticsearchClient.indices.existsIndexTemplate.mockRejectedValueOnce(unexpectedError);
mockElasticsearchClient.indices.existsIndexTemplate.mockResolvedValueOnce(
securityMock.createApiResponse({ body: true })
);

View file

@ -37,31 +37,33 @@ const SESSION_INDEX_TEMPLATE_VERSION = 1;
/**
* Returns index template that is used for the current version of the session index.
*/
export function getSessionIndexTemplate(indexName: string) {
export function getSessionIndexTemplate(templateName: string, indexName: string) {
return Object.freeze({
name: templateName,
index_patterns: [indexName],
order: 1000,
settings: {
index: {
number_of_shards: 1,
number_of_replicas: 0,
auto_expand_replicas: '0-1',
priority: 1000,
refresh_interval: '1s',
hidden: true,
template: {
settings: {
index: {
number_of_shards: 1,
number_of_replicas: 0,
auto_expand_replicas: '0-1',
priority: 1000,
refresh_interval: '1s',
hidden: true,
},
},
mappings: {
dynamic: 'strict',
properties: {
usernameHash: { type: 'keyword' },
provider: { properties: { name: { type: 'keyword' }, type: { type: 'keyword' } } },
idleTimeoutExpiration: { type: 'date' },
lifespanExpiration: { type: 'date' },
accessAgreementAcknowledged: { type: 'boolean' },
content: { type: 'binary' },
},
} as const,
},
mappings: {
dynamic: 'strict',
properties: {
usernameHash: { type: 'keyword' },
provider: { properties: { name: { type: 'keyword' }, type: { type: 'keyword' } } },
idleTimeoutExpiration: { type: 'date' },
lifespanExpiration: { type: 'date' },
accessAgreementAcknowledged: { type: 'boolean' },
content: { type: 'binary' },
},
} as const,
});
}
@ -318,11 +320,40 @@ export class SessionIndex {
const sessionIndexTemplateName = `${this.options.kibanaIndexName}_security_session_index_template_${SESSION_INDEX_TEMPLATE_VERSION}`;
return (this.indexInitialization = new Promise<void>(async (resolve, reject) => {
try {
// Check if legacy index template exists, and remove it if it does.
let legacyIndexTemplateExists = false;
try {
legacyIndexTemplateExists = (
await this.options.elasticsearchClient.indices.existsTemplate({
name: sessionIndexTemplateName,
})
).body;
} catch (err) {
this.options.logger.error(
`Failed to check if session legacy index template exists: ${err.message}`
);
return reject(err);
}
if (legacyIndexTemplateExists) {
try {
await this.options.elasticsearchClient.indices.deleteTemplate({
name: sessionIndexTemplateName,
});
this.options.logger.debug('Successfully deleted session legacy index template.');
} catch (err) {
this.options.logger.error(
`Failed to delete session legacy index template: ${err.message}`
);
return reject(err);
}
}
// Check if required index template exists.
let indexTemplateExists = false;
try {
indexTemplateExists = (
await this.options.elasticsearchClient.indices.existsTemplate({
await this.options.elasticsearchClient.indices.existsIndexTemplate({
name: sessionIndexTemplateName,
})
).body;
@ -338,10 +369,9 @@ export class SessionIndex {
this.options.logger.debug('Session index template already exists.');
} else {
try {
await this.options.elasticsearchClient.indices.putTemplate({
name: sessionIndexTemplateName,
body: getSessionIndexTemplate(this.indexName),
});
await this.options.elasticsearchClient.indices.putIndexTemplate(
getSessionIndexTemplate(sessionIndexTemplateName, this.indexName)
);
this.options.logger.debug('Successfully created session index template.');
} catch (err) {
this.options.logger.error(`Failed to create session index template: ${err.message}`);