mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Backporting https://github.com/elastic/kibana/pull/195895 into 8.16. --------- Co-authored-by: Kevin Lacabane <kevin.lacabane@elastic.co>
This commit is contained in:
parent
0e3ac4798e
commit
931579bab9
44 changed files with 559 additions and 391 deletions
|
@ -59,7 +59,7 @@ export const entityDefinitionRuntimePrivileges = {
|
|||
index: [
|
||||
{
|
||||
names: [ENTITY_INTERNAL_INDICES_PATTERN],
|
||||
privileges: ['create_index', 'index', 'create_doc', 'auto_configure', 'read'],
|
||||
privileges: ['create_index', 'delete_index', 'index', 'create_doc', 'auto_configure', 'read'],
|
||||
},
|
||||
{
|
||||
names: [...BUILT_IN_ALLOWED_INDICES, ENTITY_INTERNAL_INDICES_PATTERN],
|
||||
|
|
|
@ -29,7 +29,7 @@ export const builtInServicesFromEcsEntityDefinition: EntityDefinition =
|
|||
identityFields: ['service.name'],
|
||||
displayNameTemplate: '{{service.name}}',
|
||||
metadata: [
|
||||
{ source: '_index', destination: 'sourceIndex' },
|
||||
{ source: '_index', destination: 'source_index' },
|
||||
{
|
||||
source: 'data_stream.type',
|
||||
destination: 'source_data_stream.type',
|
||||
|
@ -38,7 +38,7 @@ export const builtInServicesFromEcsEntityDefinition: EntityDefinition =
|
|||
source: 'data_stream.dataset',
|
||||
destination: 'source_data_stream.dataset',
|
||||
},
|
||||
{ source: 'agent.name', aggregation: { type: 'terms', limit: 100 } },
|
||||
'agent.name',
|
||||
'service.environment',
|
||||
'service.name',
|
||||
'service.namespace',
|
||||
|
|
|
@ -25,7 +25,7 @@ export async function createAndInstallIngestPipelines(
|
|||
id: latestId,
|
||||
processors: latestProcessors,
|
||||
_meta: {
|
||||
definitionVersion: definition.version,
|
||||
definition_version: definition.version,
|
||||
managed: definition.managed,
|
||||
},
|
||||
}),
|
||||
|
|
|
@ -14,11 +14,13 @@ export async function deleteIndices(
|
|||
definition: EntityDefinition,
|
||||
logger: Logger
|
||||
) {
|
||||
const index = generateLatestIndexName(definition);
|
||||
try {
|
||||
const index = generateLatestIndexName(definition);
|
||||
await esClient.indices.delete({ index, ignore_unavailable: true });
|
||||
} catch (e) {
|
||||
logger.error(`Unable to remove entity definition index [${definition.id}}]`);
|
||||
logger.error(
|
||||
`Unable to remove entity definition index ${index} for definition [${definition.id}]`
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,25 +16,25 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"set": Object {
|
||||
"field": "entity.definitionId",
|
||||
"field": "entity.definition_id",
|
||||
"value": "builtin_mock_entity_definition",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"set": Object {
|
||||
"field": "entity.definitionVersion",
|
||||
"field": "entity.definition_version",
|
||||
"value": "1.0.0",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"set": Object {
|
||||
"field": "entity.schemaVersion",
|
||||
"field": "entity.schema_version",
|
||||
"value": "v1",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"set": Object {
|
||||
"field": "entity.identityFields",
|
||||
"field": "entity.identity_fields",
|
||||
"value": Array [
|
||||
"log.logger",
|
||||
],
|
||||
|
@ -92,7 +92,7 @@ if (ctx.entity?.metadata?.sourceIndex?.data != null) {
|
|||
},
|
||||
Object {
|
||||
"set": Object {
|
||||
"field": "entity.displayName",
|
||||
"field": "entity.display_name",
|
||||
"value": "{{log.logger}}",
|
||||
},
|
||||
},
|
||||
|
@ -121,25 +121,25 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"set": Object {
|
||||
"field": "entity.definitionId",
|
||||
"field": "entity.definition_id",
|
||||
"value": "admin-console-services",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"set": Object {
|
||||
"field": "entity.definitionVersion",
|
||||
"field": "entity.definition_version",
|
||||
"value": "1.0.0",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"set": Object {
|
||||
"field": "entity.schemaVersion",
|
||||
"field": "entity.schema_version",
|
||||
"value": "v1",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"set": Object {
|
||||
"field": "entity.identityFields",
|
||||
"field": "entity.identity_fields",
|
||||
"value": Array [
|
||||
"log.logger",
|
||||
],
|
||||
|
@ -197,7 +197,7 @@ if (ctx.entity?.metadata?.sourceIndex?.data != null) {
|
|||
},
|
||||
Object {
|
||||
"set": Object {
|
||||
"field": "entity.displayName",
|
||||
"field": "entity.display_name",
|
||||
"value": "{{log.logger}}",
|
||||
},
|
||||
},
|
||||
|
|
|
@ -117,25 +117,25 @@ export function generateLatestProcessors(definition: EntityDefinition) {
|
|||
},
|
||||
{
|
||||
set: {
|
||||
field: 'entity.definitionId',
|
||||
field: 'entity.definition_id',
|
||||
value: definition.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
set: {
|
||||
field: 'entity.definitionVersion',
|
||||
field: 'entity.definition_version',
|
||||
value: definition.version,
|
||||
},
|
||||
},
|
||||
{
|
||||
set: {
|
||||
field: 'entity.schemaVersion',
|
||||
field: 'entity.schema_version',
|
||||
value: ENTITY_SCHEMA_VERSION_V1,
|
||||
},
|
||||
},
|
||||
{
|
||||
set: {
|
||||
field: 'entity.identityFields',
|
||||
field: 'entity.identity_fields',
|
||||
value: definition.identityFields.map((identityField) => identityField.field),
|
||||
},
|
||||
},
|
||||
|
@ -173,7 +173,7 @@ export function generateLatestProcessors(definition: EntityDefinition) {
|
|||
// This must happen AFTER we lift the identity fields into the root of the document
|
||||
{
|
||||
set: {
|
||||
field: 'entity.displayName',
|
||||
field: 'entity.display_name',
|
||||
value: definition.displayNameTemplate,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -74,7 +74,7 @@ const assertHasCreatedDefinition = (
|
|||
id: generateLatestIngestPipelineId(definition),
|
||||
processors: expect.anything(),
|
||||
_meta: {
|
||||
definitionVersion: definition.version,
|
||||
definition_version: definition.version,
|
||||
managed: definition.managed,
|
||||
},
|
||||
});
|
||||
|
@ -112,7 +112,7 @@ const assertHasUpgradedDefinition = (
|
|||
id: generateLatestIngestPipelineId(definition),
|
||||
processors: expect.anything(),
|
||||
_meta: {
|
||||
definitionVersion: definition.version,
|
||||
definition_version: definition.version,
|
||||
managed: definition.managed,
|
||||
},
|
||||
});
|
||||
|
@ -260,7 +260,7 @@ describe('install_entity_definition', () => {
|
|||
describe('installBuiltInEntityDefinitions', () => {
|
||||
it('should install definition when not found', async () => {
|
||||
const builtInDefinitions = [mockEntityDefinition];
|
||||
const esClient = elasticsearchClientMock.createScopedClusterClient().asCurrentUser;
|
||||
const clusterClient = elasticsearchClientMock.createScopedClusterClient();
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
soClient.find.mockResolvedValue({ saved_objects: [], total: 0, page: 1, per_page: 10 });
|
||||
soClient.update.mockResolvedValue({
|
||||
|
@ -271,18 +271,19 @@ describe('install_entity_definition', () => {
|
|||
});
|
||||
|
||||
await installBuiltInEntityDefinitions({
|
||||
esClient,
|
||||
clusterClient,
|
||||
soClient,
|
||||
definitions: builtInDefinitions,
|
||||
logger: loggerMock.create(),
|
||||
});
|
||||
|
||||
assertHasCreatedDefinition(mockEntityDefinition, soClient, esClient);
|
||||
assertHasCreatedDefinition(mockEntityDefinition, soClient, clusterClient.asSecondaryAuthUser);
|
||||
});
|
||||
|
||||
it('should reinstall when partial state found', async () => {
|
||||
const builtInDefinitions = [mockEntityDefinition];
|
||||
const esClient = elasticsearchClientMock.createScopedClusterClient().asCurrentUser;
|
||||
const clusterClient = elasticsearchClientMock.createScopedClusterClient();
|
||||
const esClient = clusterClient.asInternalUser;
|
||||
// mock partially installed definition
|
||||
esClient.ingest.getPipeline.mockResolvedValue({});
|
||||
esClient.transform.getTransformStats.mockResolvedValue({ transforms: [], count: 0 });
|
||||
|
@ -314,14 +315,18 @@ describe('install_entity_definition', () => {
|
|||
});
|
||||
|
||||
await installBuiltInEntityDefinitions({
|
||||
esClient,
|
||||
clusterClient,
|
||||
soClient,
|
||||
definitions: builtInDefinitions,
|
||||
logger: loggerMock.create(),
|
||||
});
|
||||
|
||||
assertHasDeletedTransforms(mockEntityDefinition, esClient);
|
||||
assertHasUpgradedDefinition(mockEntityDefinition, soClient, esClient);
|
||||
assertHasDeletedTransforms(mockEntityDefinition, clusterClient.asSecondaryAuthUser);
|
||||
assertHasUpgradedDefinition(
|
||||
mockEntityDefinition,
|
||||
soClient,
|
||||
clusterClient.asSecondaryAuthUser
|
||||
);
|
||||
});
|
||||
|
||||
it('should reinstall when outdated version', async () => {
|
||||
|
@ -329,7 +334,8 @@ describe('install_entity_definition', () => {
|
|||
...mockEntityDefinition,
|
||||
version: semver.inc(mockEntityDefinition.version, 'major') ?? '0.0.0',
|
||||
};
|
||||
const esClient = elasticsearchClientMock.createScopedClusterClient().asCurrentUser;
|
||||
const clusterClient = elasticsearchClientMock.createScopedClusterClient();
|
||||
const esClient = clusterClient.asInternalUser;
|
||||
esClient.transform.getTransformStats.mockResolvedValue({ transforms: [], count: 0 });
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
|
||||
|
@ -359,14 +365,14 @@ describe('install_entity_definition', () => {
|
|||
});
|
||||
|
||||
await installBuiltInEntityDefinitions({
|
||||
esClient,
|
||||
clusterClient,
|
||||
soClient,
|
||||
definitions: [updatedDefinition],
|
||||
logger: loggerMock.create(),
|
||||
});
|
||||
|
||||
assertHasDeletedTransforms(mockEntityDefinition, esClient);
|
||||
assertHasUpgradedDefinition(updatedDefinition, soClient, esClient);
|
||||
assertHasDeletedTransforms(mockEntityDefinition, clusterClient.asSecondaryAuthUser);
|
||||
assertHasUpgradedDefinition(updatedDefinition, soClient, clusterClient.asSecondaryAuthUser);
|
||||
});
|
||||
|
||||
it('should reinstall when stale upgrade', async () => {
|
||||
|
@ -374,7 +380,8 @@ describe('install_entity_definition', () => {
|
|||
...mockEntityDefinition,
|
||||
version: semver.inc(mockEntityDefinition.version, 'major') ?? '0.0.0',
|
||||
};
|
||||
const esClient = elasticsearchClientMock.createScopedClusterClient().asCurrentUser;
|
||||
const clusterClient = elasticsearchClientMock.createScopedClusterClient();
|
||||
const esClient = clusterClient.asInternalUser;
|
||||
esClient.transform.getTransformStats.mockResolvedValue({ transforms: [], count: 0 });
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
|
||||
|
@ -406,18 +413,19 @@ describe('install_entity_definition', () => {
|
|||
});
|
||||
|
||||
await installBuiltInEntityDefinitions({
|
||||
esClient,
|
||||
clusterClient,
|
||||
soClient,
|
||||
definitions: [updatedDefinition],
|
||||
logger: loggerMock.create(),
|
||||
});
|
||||
|
||||
assertHasDeletedTransforms(mockEntityDefinition, esClient);
|
||||
assertHasUpgradedDefinition(updatedDefinition, soClient, esClient);
|
||||
assertHasDeletedTransforms(mockEntityDefinition, clusterClient.asSecondaryAuthUser);
|
||||
assertHasUpgradedDefinition(updatedDefinition, soClient, clusterClient.asSecondaryAuthUser);
|
||||
});
|
||||
|
||||
it('should reinstall when failed installation', async () => {
|
||||
const esClient = elasticsearchClientMock.createScopedClusterClient().asCurrentUser;
|
||||
const clusterClient = elasticsearchClientMock.createScopedClusterClient();
|
||||
const esClient = clusterClient.asInternalUser;
|
||||
esClient.transform.getTransformStats.mockResolvedValue({ transforms: [], count: 0 });
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
|
||||
|
@ -448,14 +456,18 @@ describe('install_entity_definition', () => {
|
|||
});
|
||||
|
||||
await installBuiltInEntityDefinitions({
|
||||
esClient,
|
||||
clusterClient,
|
||||
soClient,
|
||||
definitions: [mockEntityDefinition],
|
||||
logger: loggerMock.create(),
|
||||
});
|
||||
|
||||
assertHasDeletedTransforms(mockEntityDefinition, esClient);
|
||||
assertHasUpgradedDefinition(mockEntityDefinition, soClient, esClient);
|
||||
assertHasDeletedTransforms(mockEntityDefinition, clusterClient.asSecondaryAuthUser);
|
||||
assertHasUpgradedDefinition(
|
||||
mockEntityDefinition,
|
||||
soClient,
|
||||
clusterClient.asSecondaryAuthUser
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import semver from 'semver';
|
||||
import { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
|
||||
import { ElasticsearchClient, IScopedClusterClient } from '@kbn/core-elasticsearch-server';
|
||||
import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
|
||||
import { EntityDefinition, EntityDefinitionUpdate } from '@kbn/entities-schema';
|
||||
import { Logger } from '@kbn/logging';
|
||||
|
@ -29,6 +29,7 @@ import { mergeEntityDefinitionUpdate } from './helpers/merge_definition_update';
|
|||
import { EntityDefinitionWithState } from './types';
|
||||
import { stopLatestTransform, stopTransforms } from './stop_transforms';
|
||||
import { deleteLatestTransform, deleteTransforms } from './delete_transforms';
|
||||
import { deleteIndices } from './delete_index';
|
||||
|
||||
export interface InstallDefinitionParams {
|
||||
esClient: ElasticsearchClient;
|
||||
|
@ -49,10 +50,7 @@ export async function installEntityDefinition({
|
|||
validateDefinitionCanCreateValidTransformIds(definition);
|
||||
|
||||
if (await entityDefinitionExists(soClient, definition.id)) {
|
||||
throw new EntityIdConflict(
|
||||
`Entity definition with [${definition.id}] already exists.`,
|
||||
definition
|
||||
);
|
||||
throw new EntityIdConflict(`Entity definition [${definition.id}] already exists.`, definition);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -65,7 +63,7 @@ export async function installEntityDefinition({
|
|||
|
||||
return await install({ esClient, soClient, logger, definition: entityDefinition });
|
||||
} catch (e) {
|
||||
logger.error(`Failed to install entity definition ${definition.id}: ${e}`);
|
||||
logger.error(`Failed to install entity definition [${definition.id}]: ${e}`);
|
||||
|
||||
await stopLatestTransform(esClient, definition, logger);
|
||||
await deleteLatestTransform(esClient, definition, logger);
|
||||
|
@ -90,28 +88,32 @@ export async function installEntityDefinition({
|
|||
}
|
||||
|
||||
export async function installBuiltInEntityDefinitions({
|
||||
esClient,
|
||||
clusterClient,
|
||||
soClient,
|
||||
logger,
|
||||
definitions,
|
||||
}: Omit<InstallDefinitionParams, 'definition'> & {
|
||||
}: Omit<InstallDefinitionParams, 'definition' | 'esClient'> & {
|
||||
clusterClient: IScopedClusterClient;
|
||||
definitions: EntityDefinition[];
|
||||
}): Promise<EntityDefinition[]> {
|
||||
if (definitions.length === 0) return [];
|
||||
|
||||
logger.debug(`Starting installation of ${definitions.length} built-in definitions`);
|
||||
logger.info(`Checking installation of ${definitions.length} built-in definitions`);
|
||||
const installPromises = definitions.map(async (builtInDefinition) => {
|
||||
const installedDefinition = await findEntityDefinitionById({
|
||||
esClient,
|
||||
soClient,
|
||||
esClient: clusterClient.asInternalUser,
|
||||
id: builtInDefinition.id,
|
||||
includeState: true,
|
||||
});
|
||||
|
||||
if (!installedDefinition) {
|
||||
// clean data from previous installation
|
||||
await deleteIndices(clusterClient.asCurrentUser, builtInDefinition, logger);
|
||||
|
||||
return await installEntityDefinition({
|
||||
definition: builtInDefinition,
|
||||
esClient,
|
||||
esClient: clusterClient.asSecondaryAuthUser,
|
||||
soClient,
|
||||
logger,
|
||||
});
|
||||
|
@ -127,15 +129,16 @@ export async function installBuiltInEntityDefinitions({
|
|||
return installedDefinition;
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
logger.info(
|
||||
`Detected failed or outdated installation of definition [${installedDefinition.id}] v${installedDefinition.version}, installing v${builtInDefinition.version}`
|
||||
);
|
||||
return await reinstallEntityDefinition({
|
||||
soClient,
|
||||
esClient,
|
||||
clusterClient,
|
||||
logger,
|
||||
definition: installedDefinition,
|
||||
definitionUpdate: builtInDefinition,
|
||||
deleteData: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -150,22 +153,16 @@ async function install({
|
|||
definition,
|
||||
logger,
|
||||
}: InstallDefinitionParams): Promise<EntityDefinition> {
|
||||
logger.debug(
|
||||
() =>
|
||||
`Installing definition ${definition.id} v${definition.version}\n${JSON.stringify(
|
||||
definition,
|
||||
null,
|
||||
2
|
||||
)}`
|
||||
);
|
||||
logger.info(`Installing definition [${definition.id}] v${definition.version}`);
|
||||
logger.debug(() => JSON.stringify(definition, null, 2));
|
||||
|
||||
logger.debug(`Installing index templates for definition ${definition.id}`);
|
||||
logger.debug(`Installing index templates for definition [${definition.id}]`);
|
||||
const templates = await createAndInstallTemplates(esClient, definition, logger);
|
||||
|
||||
logger.debug(`Installing ingest pipelines for definition ${definition.id}`);
|
||||
logger.debug(`Installing ingest pipelines for definition [${definition.id}]`);
|
||||
const pipelines = await createAndInstallIngestPipelines(esClient, definition, logger);
|
||||
|
||||
logger.debug(`Installing transforms for definition ${definition.id}`);
|
||||
logger.debug(`Installing transforms for definition [${definition.id}]`);
|
||||
const transforms = await createAndInstallTransforms(esClient, definition, logger);
|
||||
|
||||
const updatedProps = await updateEntityDefinition(soClient, definition.id, {
|
||||
|
@ -177,20 +174,23 @@ async function install({
|
|||
|
||||
// stop and delete the current transforms and reinstall all the components
|
||||
export async function reinstallEntityDefinition({
|
||||
esClient,
|
||||
clusterClient,
|
||||
soClient,
|
||||
definition,
|
||||
definitionUpdate,
|
||||
logger,
|
||||
}: InstallDefinitionParams & {
|
||||
deleteData = false,
|
||||
}: Omit<InstallDefinitionParams, 'esClient'> & {
|
||||
clusterClient: IScopedClusterClient;
|
||||
definitionUpdate: EntityDefinitionUpdate;
|
||||
deleteData?: boolean;
|
||||
}): Promise<EntityDefinition> {
|
||||
try {
|
||||
const updatedDefinition = mergeEntityDefinitionUpdate(definition, definitionUpdate);
|
||||
|
||||
logger.debug(
|
||||
() =>
|
||||
`Reinstalling definition ${definition.id} from v${definition.version} to v${
|
||||
`Reinstalling definition [${definition.id}] from v${definition.version} to v${
|
||||
definitionUpdate.version
|
||||
}\n${JSON.stringify(updatedDefinition, null, 2)}`
|
||||
);
|
||||
|
@ -201,13 +201,17 @@ export async function reinstallEntityDefinition({
|
|||
installStartedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
logger.debug(`Deleting transforms for definition ${definition.id} v${definition.version}`);
|
||||
await stopAndDeleteTransforms(esClient, definition, logger);
|
||||
logger.debug(`Deleting transforms for definition [${definition.id}] v${definition.version}`);
|
||||
await stopAndDeleteTransforms(clusterClient.asSecondaryAuthUser, definition, logger);
|
||||
|
||||
if (deleteData) {
|
||||
await deleteIndices(clusterClient.asCurrentUser, definition, logger);
|
||||
}
|
||||
|
||||
return await install({
|
||||
esClient,
|
||||
soClient,
|
||||
logger,
|
||||
esClient: clusterClient.asSecondaryAuthUser,
|
||||
definition: updatedDefinition,
|
||||
});
|
||||
} catch (err) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
exports[`generateLatestTransform(definition) should generate a valid latest transform 1`] = `
|
||||
Object {
|
||||
"_meta": Object {
|
||||
"definitionVersion": "1.0.0",
|
||||
"definition_version": "1.0.0",
|
||||
"managed": false,
|
||||
},
|
||||
"defer_validation": true,
|
||||
|
@ -42,7 +42,7 @@ Object {
|
|||
},
|
||||
},
|
||||
},
|
||||
"entity.lastSeenTimestamp": Object {
|
||||
"entity.last_seen_timestamp": Object {
|
||||
"max": Object {
|
||||
"field": "@timestamp",
|
||||
},
|
||||
|
|
|
@ -69,7 +69,7 @@ const generateTransformPutRequest = ({
|
|||
return {
|
||||
transform_id: transformId,
|
||||
_meta: {
|
||||
definitionVersion: definition.version,
|
||||
definition_version: definition.version,
|
||||
managed: definition.managed,
|
||||
},
|
||||
defer_validation: true,
|
||||
|
@ -113,7 +113,7 @@ const generateTransformPutRequest = ({
|
|||
aggs: {
|
||||
...generateLatestMetricAggregations(definition),
|
||||
...generateLatestMetadataAggregations(definition),
|
||||
'entity.lastSeenTimestamp': {
|
||||
'entity.last_seen_timestamp': {
|
||||
max: {
|
||||
field: definition.latest.timestampField,
|
||||
},
|
||||
|
|
|
@ -35,18 +35,20 @@ export async function upgradeBuiltInEntityDefinitions({
|
|||
);
|
||||
}
|
||||
|
||||
const { esClient, soClient } = getClientsFromAPIKey({ apiKey, server });
|
||||
const { clusterClient, soClient } = getClientsFromAPIKey({ apiKey, server });
|
||||
|
||||
logger.debug(`Starting built-in definitions upgrade`);
|
||||
const upgradedDefinitions = await installBuiltInEntityDefinitions({
|
||||
esClient,
|
||||
clusterClient,
|
||||
soClient,
|
||||
definitions,
|
||||
logger,
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
upgradedDefinitions.map((definition) => startTransforms(esClient, definition, logger))
|
||||
upgradedDefinitions.map((definition) =>
|
||||
startTransforms(clusterClient.asSecondaryAuthUser, definition, logger)
|
||||
)
|
||||
);
|
||||
|
||||
return { success: true, definitions: upgradedDefinitions };
|
||||
|
|
|
@ -40,7 +40,11 @@ export class EntityClient {
|
|||
definition: EntityDefinition;
|
||||
installOnly?: boolean;
|
||||
}) {
|
||||
this.options.logger.info(
|
||||
`Creating definition [${definition.id}] v${definition.version} (installOnly=${installOnly})`
|
||||
);
|
||||
const secondaryAuthClient = this.options.clusterClient.asSecondaryAuthUser;
|
||||
|
||||
const installedDefinition = await installEntityDefinition({
|
||||
definition,
|
||||
esClient: secondaryAuthClient,
|
||||
|
@ -62,16 +66,15 @@ export class EntityClient {
|
|||
id: string;
|
||||
definitionUpdate: EntityDefinitionUpdate;
|
||||
}) {
|
||||
const secondaryAuthClient = this.options.clusterClient.asSecondaryAuthUser;
|
||||
const definition = await findEntityDefinitionById({
|
||||
id,
|
||||
soClient: this.options.soClient,
|
||||
esClient: secondaryAuthClient,
|
||||
esClient: this.options.clusterClient.asInternalUser,
|
||||
includeState: true,
|
||||
});
|
||||
|
||||
if (!definition) {
|
||||
const message = `Unable to find entity definition with [${id}]`;
|
||||
const message = `Unable to find entity definition [${id}]`;
|
||||
this.options.logger.error(message);
|
||||
throw new EntityDefinitionNotFound(message);
|
||||
}
|
||||
|
@ -86,25 +89,31 @@ export class EntityClient {
|
|||
definition as EntityDefinitionWithState
|
||||
).state.components.transforms.some((transform) => transform.running);
|
||||
|
||||
this.options.logger.info(
|
||||
`Updating definition [${definition.id}] from v${definition.version} to v${definitionUpdate.version}`
|
||||
);
|
||||
const updatedDefinition = await reinstallEntityDefinition({
|
||||
definition,
|
||||
definitionUpdate,
|
||||
soClient: this.options.soClient,
|
||||
esClient: secondaryAuthClient,
|
||||
clusterClient: this.options.clusterClient,
|
||||
logger: this.options.logger,
|
||||
});
|
||||
|
||||
if (shouldRestartTransforms) {
|
||||
await startTransforms(secondaryAuthClient, updatedDefinition, this.options.logger);
|
||||
await startTransforms(
|
||||
this.options.clusterClient.asSecondaryAuthUser,
|
||||
updatedDefinition,
|
||||
this.options.logger
|
||||
);
|
||||
}
|
||||
return updatedDefinition;
|
||||
}
|
||||
|
||||
async deleteEntityDefinition({ id, deleteData = false }: { id: string; deleteData?: boolean }) {
|
||||
const secondaryAuthClient = this.options.clusterClient.asSecondaryAuthUser;
|
||||
const definition = await findEntityDefinitionById({
|
||||
id,
|
||||
esClient: secondaryAuthClient,
|
||||
esClient: this.options.clusterClient.asInternalUser,
|
||||
soClient: this.options.soClient,
|
||||
});
|
||||
|
||||
|
@ -112,9 +121,12 @@ export class EntityClient {
|
|||
throw new EntityDefinitionNotFound(`Unable to find entity definition with [${id}]`);
|
||||
}
|
||||
|
||||
this.options.logger.info(
|
||||
`Uninstalling definition [${definition.id}] v${definition.version} (deleteData=${deleteData})`
|
||||
);
|
||||
await uninstallEntityDefinition({
|
||||
definition,
|
||||
esClient: secondaryAuthClient,
|
||||
esClient: this.options.clusterClient.asSecondaryAuthUser,
|
||||
soClient: this.options.soClient,
|
||||
logger: this.options.logger,
|
||||
});
|
||||
|
@ -146,7 +158,7 @@ export class EntityClient {
|
|||
builtIn?: boolean;
|
||||
}) {
|
||||
const definitions = await findEntityDefinitions({
|
||||
esClient: this.options.clusterClient.asSecondaryAuthUser,
|
||||
esClient: this.options.clusterClient.asInternalUser,
|
||||
soClient: this.options.soClient,
|
||||
page,
|
||||
perPage,
|
||||
|
@ -160,6 +172,7 @@ export class EntityClient {
|
|||
}
|
||||
|
||||
async startEntityDefinition(definition: EntityDefinition) {
|
||||
this.options.logger.info(`Starting transforms for definition [${definition.id}]`);
|
||||
return startTransforms(
|
||||
this.options.clusterClient.asSecondaryAuthUser,
|
||||
definition,
|
||||
|
@ -168,6 +181,7 @@ export class EntityClient {
|
|||
}
|
||||
|
||||
async stopEntityDefinition(definition: EntityDefinition) {
|
||||
this.options.logger.info(`Stopping transforms for definition [${definition.id}]`);
|
||||
return stopTransforms(
|
||||
this.options.clusterClient.asSecondaryAuthUser,
|
||||
definition,
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
IndicesPutIndexTemplateRequest,
|
||||
} from '@elastic/elasticsearch/lib/api/types';
|
||||
import { ElasticsearchClient, Logger } from '@kbn/core/server';
|
||||
import { entitiesHistoryBaseComponentTemplateConfig } from '../templates/components/base_history';
|
||||
import { entitiesLatestBaseComponentTemplateConfig } from '../templates/components/base_latest';
|
||||
import { entitiesEntityComponentTemplateConfig } from '../templates/components/entity';
|
||||
import { entitiesEventComponentTemplateConfig } from '../templates/components/event';
|
||||
|
@ -38,11 +37,6 @@ export const installEntityManagerTemplates = async ({
|
|||
logger: Logger;
|
||||
}) => {
|
||||
await Promise.all([
|
||||
upsertComponent({
|
||||
esClient,
|
||||
logger,
|
||||
component: entitiesHistoryBaseComponentTemplateConfig,
|
||||
}),
|
||||
upsertComponent({
|
||||
esClient,
|
||||
logger,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
|
||||
import { IScopedClusterClient } from '@kbn/core-elasticsearch-server';
|
||||
import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
|
||||
import { getFakeKibanaRequest } from '@kbn/security-plugin/server/authentication/api_keys/fake_kibana_request';
|
||||
import { EntityManagerServerSetup } from '../types';
|
||||
|
@ -17,9 +17,9 @@ export const getClientsFromAPIKey = ({
|
|||
}: {
|
||||
apiKey: EntityDiscoveryAPIKey;
|
||||
server: EntityManagerServerSetup;
|
||||
}): { esClient: ElasticsearchClient; soClient: SavedObjectsClientContract } => {
|
||||
}): { clusterClient: IScopedClusterClient; soClient: SavedObjectsClientContract } => {
|
||||
const fakeRequest = getFakeKibanaRequest({ id: apiKey.id, api_key: apiKey.apiKey });
|
||||
const esClient = server.core.elasticsearch.client.asScoped(fakeRequest).asSecondaryAuthUser;
|
||||
const clusterClient = server.core.elasticsearch.client.asScoped(fakeRequest);
|
||||
const soClient = server.core.savedObjects.getScopedClient(fakeRequest);
|
||||
return { esClient, soClient };
|
||||
return { clusterClient, soClient };
|
||||
};
|
||||
|
|
|
@ -61,13 +61,13 @@ export const checkEntityDiscoveryEnabledRoute = createEntityManagerServerRoute({
|
|||
return response.ok({ body: { enabled: false, reason: ERROR_API_KEY_NOT_VALID } });
|
||||
}
|
||||
|
||||
const { esClient, soClient } = getClientsFromAPIKey({ apiKey, server });
|
||||
const { clusterClient, soClient } = getClientsFromAPIKey({ apiKey, server });
|
||||
|
||||
const entityDiscoveryState = await Promise.all(
|
||||
builtInDefinitions.map(async (builtInDefinition) => {
|
||||
const definitions = await findEntityDefinitions({
|
||||
esClient,
|
||||
soClient,
|
||||
esClient: clusterClient.asSecondaryAuthUser,
|
||||
id: builtInDefinition.id,
|
||||
includeState: true,
|
||||
});
|
||||
|
|
|
@ -67,12 +67,13 @@ export const disableEntityDiscoveryRoute = createEntityManagerServerRoute({
|
|||
includedHiddenTypes: [EntityDiscoveryApiKeyType.name],
|
||||
});
|
||||
|
||||
logger.info('Disabling managed entity discovery');
|
||||
await uninstallBuiltInEntityDefinitions({
|
||||
entityClient,
|
||||
deleteData: params.query.deleteData,
|
||||
});
|
||||
|
||||
server.logger.debug('reading entity discovery API key from saved object');
|
||||
logger.debug('reading entity discovery API key from saved object');
|
||||
const apiKey = await readEntityDiscoveryAPIKey(server);
|
||||
// api key could be deleted outside of the apis, it does not affect the
|
||||
// disablement flow
|
||||
|
@ -82,6 +83,7 @@ export const disableEntityDiscoveryRoute = createEntityManagerServerRoute({
|
|||
ids: [apiKey.id],
|
||||
});
|
||||
}
|
||||
logger.info('Managed entity discovery is disabled');
|
||||
|
||||
return response.ok({ body: { success: true } });
|
||||
} catch (err) {
|
||||
|
|
|
@ -93,6 +93,7 @@ export const enableEntityDiscoveryRoute = createEntityManagerServerRoute({
|
|||
});
|
||||
}
|
||||
|
||||
logger.info(`Enabling managed entity discovery (installOnly=${params.query.installOnly})`);
|
||||
const soClient = core.savedObjects.getClient({
|
||||
includedHiddenTypes: [EntityDiscoveryApiKeyType.name],
|
||||
});
|
||||
|
@ -119,9 +120,9 @@ export const enableEntityDiscoveryRoute = createEntityManagerServerRoute({
|
|||
|
||||
await saveEntityDiscoveryAPIKey(soClient, apiKey);
|
||||
|
||||
const esClient = core.elasticsearch.client.asSecondaryAuthUser;
|
||||
const clusterClient = core.elasticsearch.client;
|
||||
const installedDefinitions = await installBuiltInEntityDefinitions({
|
||||
esClient,
|
||||
clusterClient,
|
||||
soClient,
|
||||
logger,
|
||||
definitions: builtInDefinitions,
|
||||
|
@ -130,10 +131,11 @@ export const enableEntityDiscoveryRoute = createEntityManagerServerRoute({
|
|||
if (!params.query.installOnly) {
|
||||
await Promise.all(
|
||||
installedDefinitions.map((installedDefinition) =>
|
||||
startTransforms(esClient, installedDefinition, logger)
|
||||
startTransforms(clusterClient.asSecondaryAuthUser, installedDefinition, logger)
|
||||
)
|
||||
);
|
||||
}
|
||||
logger.info('Managed entity discovery is enabled');
|
||||
|
||||
return response.ok({ body: { success: true } });
|
||||
} catch (err) {
|
||||
|
|
|
@ -5,21 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
createEntityDefinitionQuerySchema,
|
||||
entityDefinitionUpdateSchema,
|
||||
} from '@kbn/entities-schema';
|
||||
import { entityDefinitionUpdateSchema } from '@kbn/entities-schema';
|
||||
import { z } from '@kbn/zod';
|
||||
import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception';
|
||||
import { InvalidTransformError } from '../../lib/entities/errors/invalid_transform_error';
|
||||
import { findEntityDefinitionById } from '../../lib/entities/find_entity_definition';
|
||||
import { startTransforms } from '../../lib/entities/start_transforms';
|
||||
import {
|
||||
installationInProgress,
|
||||
reinstallEntityDefinition,
|
||||
} from '../../lib/entities/install_entity_definition';
|
||||
|
||||
import { createEntityManagerServerRoute } from '../create_entity_manager_server_route';
|
||||
import { EntityDefinitionNotFound } from '../../lib/entities/errors/entity_not_found';
|
||||
import { EntityDefinitionUpdateConflict } from '../../lib/entities/errors/entity_definition_update_conflict';
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
|
@ -29,13 +21,12 @@ import { createEntityManagerServerRoute } from '../create_entity_manager_server_
|
|||
* tags:
|
||||
* - definitions
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: installOnly
|
||||
* description: If true, the definition transforms will not be started
|
||||
* required: false
|
||||
* - in: path
|
||||
* name: id
|
||||
* description: The entity definition ID
|
||||
* schema:
|
||||
* type: boolean
|
||||
* default: false
|
||||
* type: string
|
||||
* required: true
|
||||
* requestBody:
|
||||
* description: The definition properties to update
|
||||
* required: true
|
||||
|
@ -63,58 +54,37 @@ export const updateEntityDefinitionRoute = createEntityManagerServerRoute({
|
|||
endpoint: 'PATCH /internal/entities/definition/{id}',
|
||||
params: z.object({
|
||||
path: z.object({ id: z.string() }),
|
||||
query: createEntityDefinitionQuerySchema,
|
||||
body: entityDefinitionUpdateSchema,
|
||||
}),
|
||||
handler: async ({ context, response, params, logger }) => {
|
||||
const core = await context.core;
|
||||
const soClient = core.savedObjects.client;
|
||||
const esClient = core.elasticsearch.client.asCurrentUser;
|
||||
handler: async ({ request, response, params, logger, getScopedClient }) => {
|
||||
const entityClient = await getScopedClient({ request });
|
||||
|
||||
try {
|
||||
const installedDefinition = await findEntityDefinitionById({
|
||||
soClient,
|
||||
esClient,
|
||||
const updatedDefinition = await entityClient.updateEntityDefinition({
|
||||
id: params.path.id,
|
||||
});
|
||||
|
||||
if (!installedDefinition) {
|
||||
return response.notFound({
|
||||
body: { message: `Entity definition [${params.path.id}] not found` },
|
||||
});
|
||||
}
|
||||
|
||||
if (installedDefinition.managed) {
|
||||
return response.forbidden({
|
||||
body: { message: `Managed definition cannot be modified` },
|
||||
});
|
||||
}
|
||||
|
||||
if (installationInProgress(installedDefinition)) {
|
||||
return response.conflict({
|
||||
body: { message: `Entity definition [${params.path.id}] has changes in progress` },
|
||||
});
|
||||
}
|
||||
|
||||
const updatedDefinition = await reinstallEntityDefinition({
|
||||
soClient,
|
||||
esClient,
|
||||
logger,
|
||||
definition: installedDefinition,
|
||||
definitionUpdate: params.body,
|
||||
});
|
||||
|
||||
if (!params.query.installOnly) {
|
||||
await startTransforms(esClient, updatedDefinition, logger);
|
||||
}
|
||||
|
||||
return response.ok({ body: updatedDefinition });
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
|
||||
if (e instanceof EntityDefinitionNotFound) {
|
||||
return response.notFound({
|
||||
body: { message: `Entity definition [${params.path.id}] not found` },
|
||||
});
|
||||
}
|
||||
|
||||
if (e instanceof EntityDefinitionUpdateConflict) {
|
||||
return response.conflict({
|
||||
body: { message: `Entity definition [${params.path.id}] has changes in progress` },
|
||||
});
|
||||
}
|
||||
|
||||
if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) {
|
||||
return response.customError({ body: e, statusCode: 400 });
|
||||
}
|
||||
|
||||
return response.customError({ body: e, statusCode: 500 });
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ClusterPutComponentTemplateRequest } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { ENTITY_HISTORY_BASE_COMPONENT_TEMPLATE_V1 } from '../../../common/constants_entities';
|
||||
|
||||
export const entitiesHistoryBaseComponentTemplateConfig: ClusterPutComponentTemplateRequest = {
|
||||
name: ENTITY_HISTORY_BASE_COMPONENT_TEMPLATE_V1,
|
||||
_meta: {
|
||||
description:
|
||||
"Component template for the ECS fields used in the Elastic Entity Model's entity discovery framework's history data set",
|
||||
documentation: 'https://www.elastic.co/guide/en/ecs/current/ecs-base.html',
|
||||
ecs_version: '8.0.0',
|
||||
managed: true,
|
||||
},
|
||||
template: {
|
||||
mappings: {
|
||||
properties: {
|
||||
'@timestamp': {
|
||||
type: 'date',
|
||||
},
|
||||
labels: {
|
||||
type: 'object',
|
||||
},
|
||||
tags: {
|
||||
ignore_above: 1024,
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
|
@ -22,7 +22,7 @@ export const entitiesLatestBaseComponentTemplateConfig: ClusterPutComponentTempl
|
|||
properties: {
|
||||
entity: {
|
||||
properties: {
|
||||
displayName: {
|
||||
display_name: {
|
||||
type: 'text',
|
||||
fields: {
|
||||
keyword: {
|
||||
|
@ -31,9 +31,6 @@ export const entitiesLatestBaseComponentTemplateConfig: ClusterPutComponentTempl
|
|||
},
|
||||
},
|
||||
},
|
||||
firstSeenTimestamp: {
|
||||
type: 'date',
|
||||
},
|
||||
},
|
||||
},
|
||||
labels: {
|
||||
|
|
|
@ -29,22 +29,22 @@ export const entitiesEntityComponentTemplateConfig: ClusterPutComponentTemplateR
|
|||
ignore_above: 1024,
|
||||
type: 'keyword',
|
||||
},
|
||||
definitionId: {
|
||||
definition_id: {
|
||||
ignore_above: 1024,
|
||||
type: 'keyword',
|
||||
},
|
||||
definitionVersion: {
|
||||
definition_version: {
|
||||
ignore_above: 1024,
|
||||
type: 'keyword',
|
||||
},
|
||||
schemaVersion: {
|
||||
schema_version: {
|
||||
ignore_above: 1024,
|
||||
type: 'keyword',
|
||||
},
|
||||
lastSeenTimestamp: {
|
||||
last_seen_timestamp: {
|
||||
type: 'date',
|
||||
},
|
||||
identityFields: {
|
||||
identity_fields: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue