Skip to content

Commit

Permalink
[Entity Analytics][Entity Store] Init: Put engine in error state if d…
Browse files Browse the repository at this point in the history
…ata view does not exist (#201140)

## Summary

Fixes a bug where if the securite default data view does not exist the
entity engine would get stuck in 'installing' status. After this fix,
the engine is put into 'error' state.

The entity engine uses the security default data view by default to
generate it's source index pattern.

## Testing

1. Delete the security default data view `security-solution-default`
2. Init the entity store
3. Observe that the entity store is in an error state (the UI should
show the error)
  • Loading branch information
hop-dev authored Nov 22, 2024
1 parent 7ce5069 commit c3c872c
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -241,19 +241,19 @@ export class EntityStoreDataClient {
) {
const setupStartTime = moment().utc().toISOString();
const { logger, namespace, appClient, dataViewsService } = this.options;
const indexPatterns = await buildIndexPatterns(namespace, appClient, dataViewsService);
try {
const indexPatterns = await buildIndexPatterns(namespace, appClient, dataViewsService);

const unitedDefinition = getUnitedEntityDefinition({
indexPatterns,
entityType,
namespace,
fieldHistoryLength,
syncDelay: `${config.syncDelay.asSeconds()}s`,
frequency: `${config.frequency.asSeconds()}s`,
});
const { entityManagerDefinition } = unitedDefinition;
const unitedDefinition = getUnitedEntityDefinition({
indexPatterns,
entityType,
namespace,
fieldHistoryLength,
syncDelay: `${config.syncDelay.asSeconds()}s`,
frequency: `${config.frequency.asSeconds()}s`,
});
const { entityManagerDefinition } = unitedDefinition;

try {
// clean up any existing entity store
await this.delete(entityType, taskManager, { deleteData: false, deleteEngine: false });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ export default ({ getService }: FtrProviderContext) => {
});
});

describe('init error handling', () => {
afterEach(async () => {
await dataView.create('security-solution');
await utils.cleanEngines();
});

it('should return "error" when the security data view does not exist', async () => {
await dataView.delete('security-solution');
await utils.initEntityEngineForEntityType('host');
await utils.waitForEngineStatus('host', 'error');
});
});

describe('enablement', () => {
afterEach(async () => {
await utils.cleanEngines();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,16 @@ export const dataViewRouteHelpersFactory = (
supertest: SuperTest.Agent,
namespace: string = 'default'
) => ({
create: (name: string) => {
create: async (name: string) => {
const { body: existingDataView, statusCode } = await supertest.get(
`/s/${namespace}/api/data_views/data_view/${name}-${namespace}`
);

if (statusCode === 200) {
// data view exists
return existingDataView;
}

return supertest
.post(`/s/${namespace}/api/data_views/data_view`)
.set('kbn-xsrf', 'foo')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const EntityStoreUtils = (
}
};

const _initEntityEngineForEntityType = async (entityType: EntityType) => {
const initEntityEngineForEntityType = async (entityType: EntityType) => {
log.info(
`Initializing engine for entity type ${entityType} in namespace ${namespace || 'default'}`
);
Expand All @@ -72,7 +72,7 @@ export const EntityStoreUtils = (
};

const initEntityEngineForEntityTypesAndWait = async (entityTypes: EntityType[]) => {
await Promise.all(entityTypes.map((entityType) => _initEntityEngineForEntityType(entityType)));
await Promise.all(entityTypes.map((entityType) => initEntityEngineForEntityType(entityType)));

await retry.waitForWithTimeout(
`Engines to start for entity types: ${entityTypes.join(', ')}`,
Expand All @@ -90,6 +90,20 @@ export const EntityStoreUtils = (
);
};

const waitForEngineStatus = async (entityType: EntityType, status: string) => {
await retry.waitForWithTimeout(
`Engine for entity type ${entityType} to be in status ${status}`,
60_000,
async () => {
const { body } = await api
.getEntityEngine({ params: { entityType } }, namespace)
.expect(200);
log.debug(`Engine status for ${entityType}: ${body.status}`);
return body.status === status;
}
);
};

const enableEntityStore = async () => {
const res = await api.initEntityStore({ body: {} }, namespace);
if (res.status !== 200) {
Expand Down Expand Up @@ -155,5 +169,7 @@ export const EntityStoreUtils = (
expectEngineAssetsExist,
expectEngineAssetsDoNotExist,
enableEntityStore,
waitForEngineStatus,
initEntityEngineForEntityType,
};
};

0 comments on commit c3c872c

Please sign in to comment.