Skip to content

Commit

Permalink
Migrate to the composable session index template on the startup. (#12…
Browse files Browse the repository at this point in the history
  • Loading branch information
azasypkin authored Dec 27, 2021
1 parent e839b00 commit 2ff66a8
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -76,10 +85,17 @@ 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('creates both index template and index if they do not exist', async () => {
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(
Expand All @@ -88,12 +104,38 @@ describe('Session index', () => {

await sessionIndex.initialize();

const expectedIndexTemplate = getSessionIndexTemplate(indexName);
const expectedIndexTemplate = getSessionIndexTemplate(indexTemplateName, indexName);
assertExistenceChecksPerformed();
expect(mockElasticsearchClient.indices.putTemplate).toHaveBeenCalledWith({
expect(mockElasticsearchClient.indices.deleteTemplate).toHaveBeenCalledWith({
name: indexTemplateName,
body: expectedIndexTemplate,
});
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(indexTemplateName, indexName);
assertExistenceChecksPerformed();
expect(mockElasticsearchClient.indices.deleteTemplate).not.toHaveBeenCalled();
expect(mockElasticsearchClient.indices.putIndexTemplate).toHaveBeenCalledWith(
expectedIndexTemplate
);
expect(mockElasticsearchClient.indices.create).toHaveBeenCalledWith({
index: expectedIndexTemplate.index_patterns[0],
});
Expand All @@ -103,21 +145,27 @@ 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 })
);

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(
Expand All @@ -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(
Expand All @@ -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 })
);

Expand Down
82 changes: 56 additions & 26 deletions x-pack/plugins/security/server/session_management/session_index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
}

Expand Down Expand Up @@ -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;
Expand All @@ -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}`);
Expand Down

0 comments on commit 2ff66a8

Please sign in to comment.