[8.16] Kibana space scoped component-template for risk engine (#197170) (#198110)

# Backport

This will backport the following commits from `main` to `8.16`:
- [Kibana space scoped component-template for risk engine
(#197170)](https://github.com/elastic/kibana/pull/197170)

<!--- Backport version: 8.9.8 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Abhishek
Bhatia","email":"117628830+abhishekbhatia1710@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-10-22T15:45:52Z","message":"Kibana
space scoped component-template for risk engine (#197170)\n\n##
Summary\r\n\r\nThe component template used when enabling the risk engine
is not Kibana\r\nspace-agnostic, as the component template in every
space is named\r\n`.risk-score-mappings`. This caused issues during the
cleanup process,\r\nwhere it attempted to delete the same component
template from each space\r\nbut failed because other spaces' index
templates were still referencing\r\nit.\r\n\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [x] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n\r\n\r\n\r\n### For
maintainers\r\n\r\n- [ ] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels)\r\n-
[ ] This will appear in the **Release Notes** and follow
the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"b562288289f326084511c68759db84e20cee2791","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","backport
missing","v9.0.0","Team:Entity
Analytics","backport:version"],"number":197170,"url":"https://github.com/elastic/kibana/pull/197170","mergeCommit":{"message":"Kibana
space scoped component-template for risk engine (#197170)\n\n##
Summary\r\n\r\nThe component template used when enabling the risk engine
is not Kibana\r\nspace-agnostic, as the component template in every
space is named\r\n`.risk-score-mappings`. This caused issues during the
cleanup process,\r\nwhere it attempted to delete the same component
template from each space\r\nbut failed because other spaces' index
templates were still referencing\r\nit.\r\n\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [x] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n\r\n\r\n\r\n### For
maintainers\r\n\r\n- [ ] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels)\r\n-
[ ] This will appear in the **Release Notes** and follow
the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"b562288289f326084511c68759db84e20cee2791"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/197170","number":197170,"mergeCommit":{"message":"Kibana
space scoped component-template for risk engine (#197170)\n\n##
Summary\r\n\r\nThe component template used when enabling the risk engine
is not Kibana\r\nspace-agnostic, as the component template in every
space is named\r\n`.risk-score-mappings`. This caused issues during the
cleanup process,\r\nwhere it attempted to delete the same component
template from each space\r\nbut failed because other spaces' index
templates were still referencing\r\nit.\r\n\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [x] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n\r\n\r\n\r\n### For
maintainers\r\n\r\n- [ ] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels)\r\n-
[ ] This will appear in the **Release Notes** and follow
the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"b562288289f326084511c68759db84e20cee2791"}}]}]
BACKPORT-->
This commit is contained in:
Abhishek Bhatia 2024-10-29 15:02:28 +05:30 committed by GitHub
parent 4c242c6b5b
commit 6f333b8ae0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 666 additions and 355 deletions

View file

@ -129,6 +129,9 @@ export const riskScoreFieldMap: FieldMap = {
} as const;
export const mappingComponentName = '.risk-score-mappings';
export const nameSpaceAwareMappingsComponentName = (namespace: string): string => {
return `${mappingComponentName}-${namespace}`;
};
export const totalFieldsLimit = 1000;
export const getIndexPatternDataStream = (namespace: string): IIndexPatternString => ({

View file

@ -40,6 +40,7 @@ jest.spyOn(transforms, 'scheduleTransformNow').mockResolvedValue(Promise.resolve
describe('RiskScoreDataClient', () => {
let riskScoreDataClient: RiskScoreDataClient;
let riskScoreDataClientWithNameSpace: RiskScoreDataClient;
let mockSavedObjectClient: ReturnType<typeof savedObjectsClientMock.create>;
let logger: ReturnType<typeof loggingSystemMock.createLogger>;
const esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser;
@ -56,6 +57,8 @@ describe('RiskScoreDataClient', () => {
namespace: 'default',
};
riskScoreDataClient = new RiskScoreDataClient(options);
const optionsWithNamespace = { ...options, namespace: 'space-1' };
riskScoreDataClientWithNameSpace = new RiskScoreDataClient(optionsWithNamespace);
});
afterEach(() => {
@ -80,369 +83,396 @@ describe('RiskScoreDataClient', () => {
});
describe('init success', () => {
it('should initialize risk engine resources', async () => {
await riskScoreDataClient.init();
it('should initialize risk engine resources in the appropriate space', async () => {
const assertComponentTemplate = (namespace: string) => {
expect(createOrUpdateComponentTemplate).toHaveBeenCalledWith(
expect.objectContaining({
logger,
esClient,
template: expect.objectContaining({
name: `.risk-score-mappings-${namespace}`,
_meta: {
managed: true,
},
}),
totalFieldsLimit: 1000,
})
);
};
expect(createOrUpdateComponentTemplate).toHaveBeenCalledWith(
expect.objectContaining({
const assertIndexTemplate = (namespace: string) => {
expect(createOrUpdateIndexTemplate).toHaveBeenCalledWith({
logger,
esClient,
template: expect.objectContaining({
name: '.risk-score-mappings',
_meta: {
managed: true,
template: {
name: `.risk-score.risk-score-${namespace}-index-template`,
body: {
data_stream: { hidden: true },
index_patterns: [`risk-score.risk-score-${namespace}`],
composed_of: [`.risk-score-mappings-${namespace}`],
template: {
lifecycle: {},
settings: {
'index.mapping.total_fields.limit': totalFieldsLimit,
},
mappings: {
dynamic: false,
_meta: {
kibana: {
version: '8.9.0',
},
managed: true,
namespace,
},
},
},
_meta: {
kibana: {
version: '8.9.0',
},
managed: true,
namespace,
},
},
}),
totalFieldsLimit: 1000,
})
);
},
});
};
const assertDataStream = (namespace: string) => {
expect(createDataStream).toHaveBeenCalledWith({
logger,
esClient,
totalFieldsLimit,
indexPatterns: {
template: `.risk-score.risk-score-${namespace}-index-template`,
alias: `risk-score.risk-score-${namespace}`,
},
});
};
const assertIndex = (namespace: string) => {
expect(createOrUpdateIndex).toHaveBeenCalledWith({
logger,
esClient,
options: {
index: `risk-score.risk-score-latest-${namespace}`,
mappings: {
dynamic: false,
properties: {
'@timestamp': {
ignore_malformed: false,
type: 'date',
},
host: {
properties: {
name: {
type: 'keyword',
},
risk: {
properties: {
calculated_level: {
type: 'keyword',
},
calculated_score: {
type: 'float',
},
calculated_score_norm: {
type: 'float',
},
category_1_count: {
type: 'long',
},
category_1_score: {
type: 'float',
},
id_field: {
type: 'keyword',
},
id_value: {
type: 'keyword',
},
inputs: {
properties: {
category: {
type: 'keyword',
},
description: {
type: 'keyword',
},
id: {
type: 'keyword',
},
index: {
type: 'keyword',
},
risk_score: {
type: 'float',
},
timestamp: {
type: 'date',
},
},
type: 'object',
},
notes: {
type: 'keyword',
},
},
type: 'object',
},
},
},
user: {
properties: {
name: {
type: 'keyword',
},
risk: {
properties: {
calculated_level: {
type: 'keyword',
},
calculated_score: {
type: 'float',
},
calculated_score_norm: {
type: 'float',
},
category_1_count: {
type: 'long',
},
category_1_score: {
type: 'float',
},
id_field: {
type: 'keyword',
},
id_value: {
type: 'keyword',
},
inputs: {
properties: {
category: {
type: 'keyword',
},
description: {
type: 'keyword',
},
id: {
type: 'keyword',
},
index: {
type: 'keyword',
},
risk_score: {
type: 'float',
},
timestamp: {
type: 'date',
},
},
type: 'object',
},
notes: {
type: 'keyword',
},
},
type: 'object',
},
},
},
},
},
},
});
};
const assertTransform = (namespace: string) => {
expect(transforms.createTransform).toHaveBeenCalledWith({
logger,
esClient,
transform: {
dest: {
index: `risk-score.risk-score-latest-${namespace}`,
},
frequency: '1h',
latest: {
sort: '@timestamp',
unique_key: ['host.name', 'user.name'],
},
source: {
index: [`risk-score.risk-score-${namespace}`],
},
sync: {
time: {
delay: '0s',
field: '@timestamp',
},
},
transform_id: `risk_score_latest_transform_${namespace}`,
settings: {
unattended: true,
},
_meta: {
version: 2,
managed: true,
managed_by: 'security-entity-analytics',
},
},
});
};
// Default namespace
esClient.cluster.existsComponentTemplate.mockResolvedValue(false);
await riskScoreDataClient.init();
assertComponentTemplate('default');
assertIndexTemplate('default');
assertDataStream('default');
assertIndex('default');
assertTransform('default');
// Space-1 namespace
esClient.cluster.existsComponentTemplate.mockResolvedValue(false);
await riskScoreDataClientWithNameSpace.init();
assertComponentTemplate('space-1');
assertIndexTemplate('space-1');
assertDataStream('space-1');
assertIndex('space-1');
assertTransform('space-1');
expect((createOrUpdateComponentTemplate as jest.Mock).mock.lastCall[0].template.template)
.toMatchInlineSnapshot(`
Object {
"mappings": Object {
"dynamic": "strict",
"properties": Object {
"@timestamp": Object {
"ignore_malformed": false,
"type": "date",
},
"host": Object {
"properties": Object {
"name": Object {
"type": "keyword",
},
"risk": Object {
"properties": Object {
"calculated_level": Object {
"type": "keyword",
},
"calculated_score": Object {
"type": "float",
},
"calculated_score_norm": Object {
"type": "float",
},
"category_1_count": Object {
"type": "long",
},
"category_1_score": Object {
"type": "float",
},
"id_field": Object {
"type": "keyword",
},
"id_value": Object {
"type": "keyword",
},
"inputs": Object {
"properties": Object {
"category": Object {
"type": "keyword",
},
"description": Object {
"type": "keyword",
},
"id": Object {
"type": "keyword",
},
"index": Object {
"type": "keyword",
},
"risk_score": Object {
"type": "float",
},
"timestamp": Object {
"type": "date",
},
Object {
"mappings": Object {
"dynamic": "strict",
"properties": Object {
"@timestamp": Object {
"ignore_malformed": false,
"type": "date",
},
"host": Object {
"properties": Object {
"name": Object {
"type": "keyword",
},
"risk": Object {
"properties": Object {
"calculated_level": Object {
"type": "keyword",
},
"calculated_score": Object {
"type": "float",
},
"calculated_score_norm": Object {
"type": "float",
},
"category_1_count": Object {
"type": "long",
},
"category_1_score": Object {
"type": "float",
},
"id_field": Object {
"type": "keyword",
},
"id_value": Object {
"type": "keyword",
},
"inputs": Object {
"properties": Object {
"category": Object {
"type": "keyword",
},
"type": "object",
},
"notes": Object {
"type": "keyword",
},
},
"type": "object",
},
},
},
"user": Object {
"properties": Object {
"name": Object {
"type": "keyword",
},
"risk": Object {
"properties": Object {
"calculated_level": Object {
"type": "keyword",
},
"calculated_score": Object {
"type": "float",
},
"calculated_score_norm": Object {
"type": "float",
},
"category_1_count": Object {
"type": "long",
},
"category_1_score": Object {
"type": "float",
},
"id_field": Object {
"type": "keyword",
},
"id_value": Object {
"type": "keyword",
},
"inputs": Object {
"properties": Object {
"category": Object {
"type": "keyword",
},
"description": Object {
"type": "keyword",
},
"id": Object {
"type": "keyword",
},
"index": Object {
"type": "keyword",
},
"risk_score": Object {
"type": "float",
},
"timestamp": Object {
"type": "date",
},
"description": Object {
"type": "keyword",
},
"id": Object {
"type": "keyword",
},
"index": Object {
"type": "keyword",
},
"risk_score": Object {
"type": "float",
},
"timestamp": Object {
"type": "date",
},
"type": "object",
},
"notes": Object {
"type": "keyword",
},
"type": "object",
},
"notes": Object {
"type": "keyword",
},
"type": "object",
},
"type": "object",
},
},
},
"user": Object {
"properties": Object {
"name": Object {
"type": "keyword",
},
"risk": Object {
"properties": Object {
"calculated_level": Object {
"type": "keyword",
},
"calculated_score": Object {
"type": "float",
},
"calculated_score_norm": Object {
"type": "float",
},
"category_1_count": Object {
"type": "long",
},
"category_1_score": Object {
"type": "float",
},
"id_field": Object {
"type": "keyword",
},
"id_value": Object {
"type": "keyword",
},
"inputs": Object {
"properties": Object {
"category": Object {
"type": "keyword",
},
"description": Object {
"type": "keyword",
},
"id": Object {
"type": "keyword",
},
"index": Object {
"type": "keyword",
},
"risk_score": Object {
"type": "float",
},
"timestamp": Object {
"type": "date",
},
},
"type": "object",
},
"notes": Object {
"type": "keyword",
},
},
"type": "object",
},
},
},
},
"settings": Object {},
}
`);
expect(createOrUpdateIndexTemplate).toHaveBeenCalledWith({
logger,
esClient,
template: {
name: '.risk-score.risk-score-default-index-template',
body: {
data_stream: { hidden: true },
index_patterns: ['risk-score.risk-score-default'],
composed_of: ['.risk-score-mappings'],
template: {
lifecycle: {},
settings: {
'index.mapping.total_fields.limit': totalFieldsLimit,
},
mappings: {
dynamic: false,
_meta: {
kibana: {
version: '8.9.0',
},
managed: true,
namespace: 'default',
},
},
},
_meta: {
kibana: {
version: '8.9.0',
},
managed: true,
namespace: 'default',
},
},
},
});
expect(createDataStream).toHaveBeenCalledWith({
logger,
esClient,
totalFieldsLimit,
indexPatterns: {
template: `.risk-score.risk-score-default-index-template`,
alias: `risk-score.risk-score-default`,
},
});
expect(createOrUpdateIndex).toHaveBeenCalledWith({
logger,
esClient,
options: {
index: `risk-score.risk-score-latest-default`,
mappings: {
dynamic: false,
properties: {
'@timestamp': {
ignore_malformed: false,
type: 'date',
},
host: {
properties: {
name: {
type: 'keyword',
},
risk: {
properties: {
calculated_level: {
type: 'keyword',
},
calculated_score: {
type: 'float',
},
calculated_score_norm: {
type: 'float',
},
category_1_count: {
type: 'long',
},
category_1_score: {
type: 'float',
},
id_field: {
type: 'keyword',
},
id_value: {
type: 'keyword',
},
inputs: {
properties: {
category: {
type: 'keyword',
},
description: {
type: 'keyword',
},
id: {
type: 'keyword',
},
index: {
type: 'keyword',
},
risk_score: {
type: 'float',
},
timestamp: {
type: 'date',
},
},
type: 'object',
},
notes: {
type: 'keyword',
},
},
type: 'object',
},
},
},
user: {
properties: {
name: {
type: 'keyword',
},
risk: {
properties: {
calculated_level: {
type: 'keyword',
},
calculated_score: {
type: 'float',
},
calculated_score_norm: {
type: 'float',
},
category_1_count: {
type: 'long',
},
category_1_score: {
type: 'float',
},
id_field: {
type: 'keyword',
},
id_value: {
type: 'keyword',
},
inputs: {
properties: {
category: {
type: 'keyword',
},
description: {
type: 'keyword',
},
id: {
type: 'keyword',
},
index: {
type: 'keyword',
},
risk_score: {
type: 'float',
},
timestamp: {
type: 'date',
},
},
type: 'object',
},
notes: {
type: 'keyword',
},
},
type: 'object',
},
},
},
},
},
},
});
expect(transforms.createTransform).toHaveBeenCalledWith({
logger,
esClient,
transform: {
dest: {
index: 'risk-score.risk-score-latest-default',
},
frequency: '1h',
latest: {
sort: '@timestamp',
unique_key: ['host.name', 'user.name'],
},
source: {
index: ['risk-score.risk-score-default'],
},
sync: {
time: {
delay: '0s',
field: '@timestamp',
},
},
transform_id: 'risk_score_latest_transform_default',
settings: {
unattended: true,
},
_meta: {
version: 2,
managed: true,
managed_by: 'security-entity-analytics',
},
},
});
"settings": Object {},
}
`);
});
});
@ -479,7 +509,7 @@ describe('RiskScoreDataClient', () => {
expect(esClient.transform.deleteTransform).toHaveBeenCalledTimes(1);
expect(esClient.indices.deleteDataStream).toHaveBeenCalledTimes(1);
expect(esClient.indices.deleteIndexTemplate).toHaveBeenCalledTimes(1);
expect(esClient.cluster.deleteComponentTemplate).toHaveBeenCalledTimes(1);
expect(esClient.cluster.deleteComponentTemplate).toHaveBeenCalledTimes(2);
expect(errors).toEqual([]);
});

View file

@ -22,6 +22,7 @@ import {
getIndexPatternDataStream,
getTransformOptions,
mappingComponentName,
nameSpaceAwareMappingsComponentName,
riskScoreFieldMap,
totalFieldsLimit,
} from './configurations';
@ -114,12 +115,42 @@ export class RiskScoreDataClient {
namespace,
};
// Check if there are any existing component templates with the namespace in the name
const oldComponentTemplateExists = await esClient.cluster.existsComponentTemplate({
name: mappingComponentName,
});
// If present then copy the contents to a new component template with the namespace in the name
if (oldComponentTemplateExists) {
const oldComponentTemplateResponse = await esClient.cluster.getComponentTemplate(
{
name: mappingComponentName,
},
{ ignore: [404] }
);
const oldComponentTemplate = oldComponentTemplateResponse?.component_templates[0];
const newComponentTemplateName = nameSpaceAwareMappingsComponentName(namespace);
await esClient.cluster.putComponentTemplate({
name: newComponentTemplateName,
body: oldComponentTemplate.component_template,
});
}
// Delete the component template without the namespace in the name
await esClient.cluster.deleteComponentTemplate(
{
name: mappingComponentName,
},
{ ignore: [404] }
);
// Update the new component template with the required data
await Promise.all([
createOrUpdateComponentTemplate({
logger: this.options.logger,
esClient,
template: {
name: mappingComponentName,
name: nameSpaceAwareMappingsComponentName(namespace),
_meta: {
managed: true,
},
@ -132,6 +163,7 @@ export class RiskScoreDataClient {
}),
]);
// Reference the new component template in the index template
await createOrUpdateIndexTemplate({
logger: this.options.logger,
esClient,
@ -140,7 +172,7 @@ export class RiskScoreDataClient {
body: {
data_stream: { hidden: true },
index_patterns: [indexPatterns.alias],
composed_of: [mappingComponentName],
composed_of: [nameSpaceAwareMappingsComponentName(namespace)],
template: {
lifecycle: {},
settings: {
@ -235,6 +267,15 @@ export class RiskScoreDataClient {
)
.catch(addError);
await esClient.cluster
.deleteComponentTemplate(
{
name: nameSpaceAwareMappingsComponentName(namespace),
},
{ ignore: [404] }
)
.catch(addError);
await esClient.cluster
.deleteComponentTemplate(
{

View file

@ -27,19 +27,30 @@ export default ({ getService }: FtrProviderContext) => {
const es = getService('es');
const supertest = getService('supertest');
const kibanaServer = getService('kibanaServer');
const spaces = getService('spaces');
const customSpaceName = 'ea-customspace-it';
const riskEngineRoutes = riskEngineRouteHelpersFactory(supertest);
const riskEngineRoutesWithNamespace = riskEngineRouteHelpersFactory(supertest, customSpaceName);
const log = getService('log');
// Failing: See https://github.com/elastic/kibana/issues/191637
describe.skip('@ess @serverless @serverlessQA init_and_status_apis', () => {
describe('@ess @serverless @serverlessQA init_and_status_apis', () => {
before(async () => {
await spaces.create({
id: customSpaceName,
name: customSpaceName,
description: 'Space for ${customSpaceName}',
disabledFeatures: [],
});
await riskEngineRoutes.cleanUp();
await riskEngineRoutesWithNamespace.cleanUp();
});
afterEach(async () => {
await riskEngineRoutes.cleanUp();
await riskEngineRoutesWithNamespace.cleanUp();
await clearLegacyTransforms({ es, log });
await clearLegacyDashboards({ supertest, log });
await spaces.delete(customSpaceName);
});
describe('init api', () => {
@ -54,10 +65,21 @@ export default ({ getService }: FtrProviderContext) => {
risk_engine_resources_installed: true,
},
});
const customNamespaceResponse = await riskEngineRoutesWithNamespace.init();
expect(customNamespaceResponse.body).to.eql({
result: {
errors: [],
legacy_risk_engine_disabled: true,
risk_engine_configuration_created: true,
risk_engine_enabled: true,
risk_engine_resources_installed: true,
},
});
});
it('should install resources on init call', async () => {
const componentTemplateName = '.risk-score-mappings';
it('should install resources on init call in the default namespace', async () => {
const componentTemplateName = '.risk-score-mappings-default';
const indexTemplateName = '.risk-score.risk-score-default-index-template';
const dataStreamName = 'risk-score.risk-score-default';
const latestIndexName = 'risk-score.risk-score-latest-default';
@ -210,7 +232,7 @@ export default ({ getService }: FtrProviderContext) => {
expect(indexTemplate.index_template.index_patterns).to.eql([
'risk-score.risk-score-default',
]);
expect(indexTemplate.index_template.composed_of).to.eql(['.risk-score-mappings']);
expect(indexTemplate.index_template.composed_of).to.eql(['.risk-score-mappings-default']);
expect(indexTemplate.index_template.template!.mappings?.dynamic).to.eql(false);
expect(indexTemplate.index_template.template!.mappings?._meta?.managed).to.eql(true);
expect(indexTemplate.index_template.template!.mappings?._meta?.namespace).to.eql('default');
@ -267,6 +289,221 @@ export default ({ getService }: FtrProviderContext) => {
expect(transformStats.transforms[0].state).to.eql('stopped');
});
it('should install resources on init call in the custom namespace', async () => {
const componentTemplateName = `.risk-score-mappings-${customSpaceName}`;
const indexTemplateName = `.risk-score.risk-score-${customSpaceName}-index-template`;
const dataStreamName = `risk-score.risk-score-${customSpaceName}`;
const latestIndexName = `risk-score.risk-score-latest-${customSpaceName}`;
const transformId = `risk_score_latest_transform_${customSpaceName}`;
await riskEngineRoutesWithNamespace.init();
const { component_templates: componentTemplates1 } = await es.cluster.getComponentTemplate({
name: componentTemplateName,
});
expect(componentTemplates1.length).to.eql(1);
const componentTemplate = componentTemplates1[0];
expect(componentTemplate.name).to.eql(componentTemplateName);
expect(componentTemplate.component_template.template.mappings).to.eql({
dynamic: 'strict',
properties: {
'@timestamp': {
ignore_malformed: false,
type: 'date',
},
host: {
properties: {
name: {
type: 'keyword',
},
risk: {
properties: {
calculated_level: {
type: 'keyword',
},
calculated_score: {
type: 'float',
},
calculated_score_norm: {
type: 'float',
},
category_1_count: {
type: 'long',
},
category_1_score: {
type: 'float',
},
id_field: {
type: 'keyword',
},
id_value: {
type: 'keyword',
},
notes: {
type: 'keyword',
},
inputs: {
properties: {
id: {
type: 'keyword',
},
index: {
type: 'keyword',
},
category: {
type: 'keyword',
},
description: {
type: 'keyword',
},
risk_score: {
type: 'float',
},
timestamp: {
type: 'date',
},
},
type: 'object',
},
},
type: 'object',
},
},
},
user: {
properties: {
name: {
type: 'keyword',
},
risk: {
properties: {
calculated_level: {
type: 'keyword',
},
calculated_score: {
type: 'float',
},
calculated_score_norm: {
type: 'float',
},
category_1_count: {
type: 'long',
},
category_1_score: {
type: 'float',
},
id_field: {
type: 'keyword',
},
id_value: {
type: 'keyword',
},
notes: {
type: 'keyword',
},
inputs: {
properties: {
id: {
type: 'keyword',
},
index: {
type: 'keyword',
},
category: {
type: 'keyword',
},
description: {
type: 'keyword',
},
risk_score: {
type: 'float',
},
timestamp: {
type: 'date',
},
},
type: 'object',
},
},
type: 'object',
},
},
},
},
});
const { index_templates: indexTemplates } = await es.indices.getIndexTemplate({
name: indexTemplateName,
});
expect(indexTemplates.length).to.eql(1);
const indexTemplate = indexTemplates[0];
expect(indexTemplate.name).to.eql(indexTemplateName);
expect(indexTemplate.index_template.index_patterns).to.eql([
`risk-score.risk-score-${customSpaceName}`,
]);
expect(indexTemplate.index_template.composed_of).to.eql([
`.risk-score-mappings-${customSpaceName}`,
]);
expect(indexTemplate.index_template.template!.mappings?.dynamic).to.eql(false);
expect(indexTemplate.index_template.template!.mappings?._meta?.managed).to.eql(true);
expect(indexTemplate.index_template.template!.mappings?._meta?.namespace).to.eql(
customSpaceName
);
expect(indexTemplate.index_template.template!.mappings?._meta?.kibana?.version).to.be.a(
'string'
);
expect(indexTemplate.index_template.template!.settings).to.eql({
index: {
mapping: {
total_fields: {
limit: '1000',
},
},
},
});
expect(indexTemplate.index_template.template!.lifecycle).to.eql({
enabled: true,
});
const dsResponse = await es.indices.get({
index: dataStreamName,
});
const dataStream = Object.values(dsResponse).find(
(ds) => ds.data_stream === dataStreamName
);
expect(dataStream?.mappings?._meta?.managed).to.eql(true);
expect(dataStream?.mappings?._meta?.namespace).to.eql(customSpaceName);
expect(dataStream?.mappings?._meta?.kibana?.version).to.be.a('string');
expect(dataStream?.mappings?.dynamic).to.eql('false');
expect(dataStream?.settings?.index?.mapping).to.eql({
total_fields: {
limit: '1000',
},
});
expect(dataStream?.settings?.index?.hidden).to.eql('true');
expect(dataStream?.settings?.index?.number_of_shards).to.eql(1);
const indexExist = await es.indices.exists({
index: latestIndexName,
});
expect(indexExist).to.eql(true);
const transformStats = await es.transform.getTransformStats({
transform_id: transformId,
});
expect(transformStats.transforms[0].state).to.eql('stopped');
});
it('should create configuration saved object', async () => {
await riskEngineRoutes.init();
const response = await kibanaServer.savedObjects.find({