Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EPM][Security Solution] Implementing dataset component templates #70517

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions x-pack/plugins/ingest_manager/common/types/models/epm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ export interface Dataset {
package: string;
path: string;
ingest_pipeline: string;
elasticsearch?: RegistryElasticsearch;
}

export interface RegistryElasticsearch {
'index_template.settings'?: object;
'index_template.mappings'?: object;
}

// EPR types this as `[]map[string]interface{}`
Expand Down Expand Up @@ -272,6 +278,7 @@ export interface IndexTemplate {
data_stream: {
timestamp_field: string;
};
composed_of: string[];
_meta: object;
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
*/

import Boom from 'boom';
import { Dataset, RegistryPackage, ElasticsearchAssetType, TemplateRef } from '../../../../types';
import {
Dataset,
RegistryPackage,
ElasticsearchAssetType,
TemplateRef,
RegistryElasticsearch,
} from '../../../../types';
import { CallESAsCurrentUser } from '../../../../types';
import { Field, loadFieldsFromYaml, processFields } from '../../fields/field';
import { getPipelineNameForInstallation } from '../ingest_pipeline/install';
Expand Down Expand Up @@ -157,6 +163,98 @@ export async function installTemplateForDataset({
});
}

function putComponentTemplate(
body: object | undefined,
name: string,
callCluster: CallESAsCurrentUser
): { clusterPromise: Promise<any>; name: string } | undefined {
if (body) {
const callClusterParams: {
method: string;
path: string;
ignore: number[];
body: any;
} = {
method: 'PUT',
path: `/_component_template/${name}`,
ignore: [404],
body,
};

return { clusterPromise: callCluster('transport.request', callClusterParams), name };
}
}

function buildComponentTemplates(registryElasticsearch: RegistryElasticsearch | undefined) {
let mappingsTemplate;
let settingsTemplate;

if (registryElasticsearch && registryElasticsearch['index_template.mappings']) {
mappingsTemplate = {
template: {
mappings: {
...registryElasticsearch['index_template.mappings'],
// temporary change until https://github.com/elastic/elasticsearch/issues/58956 is resolved
// hopefully we'll be able to remove the entire properties section once that issue is resolved
properties: {
// if the timestamp_field changes here: https://github.com/elastic/kibana/blob/master/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts#L309
// we'll need to update this as well
'@timestamp': {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'll be able to remove this once this: elastic/elasticsearch#58642 is merged.

type: 'date',
},
},
},
},
};
}

if (registryElasticsearch && registryElasticsearch['index_template.settings']) {
settingsTemplate = {
template: {
settings: registryElasticsearch['index_template.settings'],
},
};
}
return { settingsTemplate, mappingsTemplate };
}

async function installDatasetComponentTemplates(
templateName: string,
registryElasticsearch: RegistryElasticsearch | undefined,
callCluster: CallESAsCurrentUser
) {
const templates: string[] = [];
const componentPromises: Array<Promise<any>> = [];

const compTemplates = buildComponentTemplates(registryElasticsearch);

const mappings = putComponentTemplate(
compTemplates.mappingsTemplate,
`${templateName}-mappings`,
callCluster
);

const settings = putComponentTemplate(
compTemplates.settingsTemplate,
`${templateName}-settings`,
callCluster
);

if (mappings) {
templates.push(mappings.name);
componentPromises.push(mappings.clusterPromise);
}

if (settings) {
templates.push(settings.name);
componentPromises.push(settings.clusterPromise);
}

// TODO: Check return values for errors
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add/link a ticket?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. I created a new ticket here: #70586

I'll update the second link once this PR is merged.

await Promise.all(componentPromises);
return templates;
}

export async function installTemplate({
callCluster,
fields,
Expand All @@ -180,13 +278,22 @@ export async function installTemplate({
packageVersion,
});
}

const composedOfTemplates = await installDatasetComponentTemplates(
templateName,
dataset.elasticsearch,
callCluster
);

const template = getTemplate({
type: dataset.type,
templateName,
mappings,
pipelineName,
packageName,
composedOfTemplates,
});

// TODO: Check return values for errors
const callClusterParams: {
method: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,37 @@ test('get template', () => {
templateName,
packageName: 'nginx',
mappings: { properties: {} },
composedOfTemplates: [],
});
expect(template.index_patterns).toStrictEqual([`${templateName}-*`]);
});

test('adds composed_of correctly', () => {
const composedOfTemplates = ['component1', 'component2'];

const template = getTemplate({
type: 'logs',
templateName: 'name',
packageName: 'nginx',
mappings: { properties: {} },
composedOfTemplates,
});
expect(template.composed_of).toStrictEqual(composedOfTemplates);
});

test('adds empty composed_of correctly', () => {
const composedOfTemplates: string[] = [];

const template = getTemplate({
type: 'logs',
templateName: 'name',
packageName: 'nginx',
mappings: { properties: {} },
composedOfTemplates,
});
expect(template.composed_of).toStrictEqual(composedOfTemplates);
});

test('tests loading base.yml', () => {
const ymlPath = path.join(__dirname, '../../fields/tests/base.yml');
const fieldsYML = readFileSync(ymlPath, 'utf-8');
Expand All @@ -45,6 +72,7 @@ test('tests loading base.yml', () => {
templateName: 'foo',
packageName: 'nginx',
mappings,
composedOfTemplates: [],
});

expect(template).toMatchSnapshot(path.basename(ymlPath));
Expand All @@ -62,6 +90,7 @@ test('tests loading coredns.logs.yml', () => {
templateName: 'foo',
packageName: 'coredns',
mappings,
composedOfTemplates: [],
});

expect(template).toMatchSnapshot(path.basename(ymlPath));
Expand All @@ -79,6 +108,7 @@ test('tests loading system.yml', () => {
templateName: 'whatsthis',
packageName: 'system',
mappings,
composedOfTemplates: [],
});

expect(template).toMatchSnapshot(path.basename(ymlPath));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,16 @@ export function getTemplate({
mappings,
pipelineName,
packageName,
composedOfTemplates,
}: {
type: string;
templateName: string;
mappings: IndexTemplateMappings;
pipelineName?: string | undefined;
packageName: string;
composedOfTemplates: string[];
}): IndexTemplate {
const template = getBaseTemplate(type, templateName, mappings, packageName);
const template = getBaseTemplate(type, templateName, mappings, packageName, composedOfTemplates);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 params is hefty, but seemingly not introduced here and can be reduced later

if (pipelineName) {
template.template.settings.index.default_pipeline = pipelineName;
}
Expand Down Expand Up @@ -244,7 +246,8 @@ function getBaseTemplate(
type: string,
templateName: string,
mappings: IndexTemplateMappings,
packageName: string
packageName: string,
composedOfTemplates: string[]
): IndexTemplate {
return {
// This takes precedence over all index templates installed by ES by default (logs-*-* and metrics-*-*)
Expand Down Expand Up @@ -308,6 +311,7 @@ function getBaseTemplate(
data_stream: {
timestamp_field: '@timestamp',
},
composed_of: composedOfTemplates,
_meta: {
package: {
name: packageName,
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/ingest_manager/server/types/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export {
PackageInfo,
RegistryVarsEntry,
Dataset,
RegistryElasticsearch,
AssetReference,
ElasticsearchAssetType,
IngestAssetType,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
- name: dataset.type
type: constant_keyword
description: >
Dataset type.
- name: dataset.name
type: constant_keyword
description: >
Dataset name.
- name: dataset.namespace
type: constant_keyword
description: >
Dataset namespace.
- name: '@timestamp'
type: date
description: >
Event timestamp.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
title: Test Dataset

type: logs

elasticsearch:
index_template.mappings:
dynamic: false
index_template.settings:
index.lifecycle.name: reference
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Test package

For testing the that the settings and mappings section get used
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
format_version: 1.0.0
name: overrides
title: Mappings Settings Test
description: This is a test package for testing that the mappings and settings sections in the dataset manifest are applied.
version: 0.1.0
categories: ['security']
release: beta
type: integration
license: basic

requirement:
elasticsearch:
versions: '>7.7.0'
kibana:
versions: '>7.7.0'

icons:
- src: '/img/logo_overrides_64_color.svg'
size: '16x16'
type: 'image/svg+xml'
1 change: 1 addition & 0 deletions x-pack/test/ingest_manager_api_integration/apis/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export default function ({ loadTestFile }) {
loadTestFile(require.resolve('./file'));
//loadTestFile(require.resolve('./template'));
loadTestFile(require.resolve('./ilm'));
loadTestFile(require.resolve('./install'));
});
}
Loading