-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
913 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// /* | ||
// * 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 { PluginInitializerContext, Plugin, CoreSetup } from 'src/core/server'; | ||
// import { PluginSetupContract as AlertingPluginSetupContract } from '../../alerting/server'; | ||
// import { BaseRuleFieldMap, baseRuleFieldMap } from '../common'; | ||
// import { EventLogService, Schema, defaultIlmPolicy } from './event_log'; | ||
// import { RuleRegistry } from './rule_registry_v2'; | ||
// import { RuleRegistryConfig } from '.'; | ||
|
||
// export type RuleRegistryPluginSetupContract = RuleRegistry<BaseRuleFieldMap>; | ||
|
||
// export class RuleRegistryPlugin implements Plugin<RuleRegistryPluginSetupContract> { | ||
// private readonly initContext: PluginInitializerContext; | ||
// private eventLogService: EventLogService | null; | ||
|
||
// constructor(initContext: PluginInitializerContext) { | ||
// this.initContext = initContext; | ||
// this.eventLogService = null; | ||
// } | ||
|
||
// public setup( | ||
// core: CoreSetup, | ||
// plugins: { alerting: AlertingPluginSetupContract } | ||
// ): RuleRegistryPluginSetupContract { | ||
// const globalConfig = this.initContext.config.legacy.get(); | ||
// const config = this.initContext.config.get<RuleRegistryConfig>(); | ||
// const logger = this.initContext.logger.get(); | ||
|
||
// this.eventLogService = new EventLogService({ | ||
// config: { | ||
// kibanaIndexName: globalConfig.kibana.index, | ||
// kibanaVersion: this.initContext.env.packageInfo.version, | ||
// isWriteEnabled: config.unsafe.write.enabled, | ||
// }, | ||
// dependencies: { | ||
// clusterClient: core.getStartServices().then(([{ elasticsearch }]) => elasticsearch.client), | ||
// logger: logger.get('event-log'), | ||
// }, | ||
// }); | ||
|
||
// const rootLogProvider = this.eventLogService.registerLog({ | ||
// name: 'alerting', | ||
// schema: Schema.create(baseRuleFieldMap), | ||
// ilmPolicy: defaultIlmPolicy, | ||
// }); | ||
|
||
// const alertsLogProvider = rootLogProvider.registerLog({ | ||
// name: 'alerts', | ||
// schema: Schema.create(baseRuleFieldMap), | ||
// }); | ||
|
||
// const executionLogProvider = rootLogProvider.registerLog({ | ||
// name: 'execlog', | ||
// schema: Schema.create(baseRuleFieldMap), | ||
// }); | ||
|
||
// const rootRegistry = new RuleRegistry({ | ||
// alertingPlugin: plugins.alerting, | ||
// alertsLogProvider, | ||
// executionLogProvider, | ||
// logger: logger.get('root'), | ||
// writeEnabled: config.unsafe.write.enabled, | ||
// }); | ||
|
||
// return rootRegistry; | ||
// } | ||
|
||
// public start() { | ||
// if (this.eventLogService) { | ||
// this.eventLogService.start(); | ||
// } | ||
// } | ||
|
||
// public stop() { | ||
// if (this.eventLogService) { | ||
// this.eventLogService.stop(); | ||
// } | ||
// } | ||
// } |
181 changes: 181 additions & 0 deletions
181
...plugins/rule_registry/server/rule_registry_v2/create_scoped_rule_registry_client/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
/* | ||
* 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 { Either, isLeft, isRight } from 'fp-ts/lib/Either'; | ||
import { Errors } from 'io-ts'; | ||
import { PathReporter } from 'io-ts/lib/PathReporter'; | ||
import { Logger } from 'kibana/server'; | ||
import { IScopedClusterClient as ScopedClusterClient } from 'src/core/server'; | ||
import { castArray, compact } from 'lodash'; | ||
import { ESSearchRequest } from 'typings/elasticsearch'; | ||
import { IndexPatternsFetcher } from '../../../../../../src/plugins/data/server'; | ||
import { TypeOfFieldMap } from '../../../common'; | ||
import { ScopedRuleRegistryClient, EventsOf } from './types'; | ||
import { BaseRuleFieldMap } from '../../../common'; | ||
import { RuleRegistry } from '..'; | ||
import { IEventLogProvider } from '../../event_log'; | ||
|
||
const createPathReporterError = (either: Either<Errors, unknown>) => { | ||
const error = new Error(`Failed to validate alert event`); | ||
error.stack += '\n' + PathReporter.report(either).join('\n'); | ||
return error; | ||
}; | ||
|
||
export function createScopedRuleRegistryClient<TFieldMap extends BaseRuleFieldMap>({ | ||
ruleUuids, | ||
scopedClusterClient, | ||
eventLogProvider, | ||
logger, | ||
registry, | ||
ruleData, | ||
}: { | ||
ruleUuids: string[]; | ||
scopedClusterClient: ScopedClusterClient; | ||
eventLogProvider: IEventLogProvider<TFieldMap>; | ||
logger: Logger; | ||
registry: RuleRegistry<TFieldMap>; | ||
ruleData?: { | ||
rule: { | ||
id: string; | ||
uuid: string; | ||
category: string; | ||
name: string; | ||
}; | ||
producer: string; | ||
tags: string[]; | ||
}; | ||
}): ScopedRuleRegistryClient<TFieldMap> { | ||
const fieldmapType = registry.getFieldMapType(); | ||
|
||
const defaults = ruleData | ||
? { | ||
'rule.uuid': ruleData.rule.uuid, | ||
'rule.id': ruleData.rule.id, | ||
'rule.name': ruleData.rule.name, | ||
'rule.category': ruleData.rule.category, | ||
'kibana.rac.producer': ruleData.producer, | ||
tags: ruleData.tags, | ||
} | ||
: {}; | ||
|
||
const client: ScopedRuleRegistryClient<BaseRuleFieldMap> = { | ||
search: async (searchRequest) => { | ||
const fields = [ | ||
'rule.id', | ||
...(searchRequest.body?.fields ? castArray(searchRequest.body.fields) : []), | ||
]; | ||
|
||
const eventLog = await eventLogProvider.getLog(); | ||
const query = eventLog.getEvents().buildQuery(); | ||
|
||
const response = await query.search({ | ||
...searchRequest, | ||
body: { | ||
...searchRequest.body, | ||
query: { | ||
bool: { | ||
filter: [ | ||
{ terms: { 'rule.uuid': ruleUuids } }, | ||
...compact([searchRequest.body?.query]), | ||
], | ||
}, | ||
}, | ||
fields, | ||
}, | ||
}); | ||
|
||
return { | ||
body: response.body as any, | ||
events: compact( | ||
response.body.hits.hits.map((hit) => { | ||
const ruleTypeId: string = hit.fields!['rule.id'][0]; | ||
|
||
const registryOfType = registry.getRegistryByRuleTypeId(ruleTypeId); | ||
|
||
if (ruleTypeId && !registryOfType) { | ||
logger.warn( | ||
`Could not find type ${ruleTypeId} in registry, decoding with default type` | ||
); | ||
} | ||
|
||
const type = registryOfType?.getFieldMapType() ?? fieldmapType; | ||
|
||
const validation = type.decode(hit.fields); | ||
if (isLeft(validation)) { | ||
const error = createPathReporterError(validation); | ||
logger.error(error); | ||
return undefined; | ||
} | ||
return type.encode(validation.right); | ||
}) | ||
) as EventsOf<ESSearchRequest, TFieldMap>, | ||
}; | ||
}, | ||
getDynamicIndexPattern: async () => { | ||
const { logIndexBasePattern } = eventLogProvider.getIndexSpec().indexNames; | ||
|
||
const indexPatternsFetcher = new IndexPatternsFetcher(scopedClusterClient.asInternalUser); | ||
const fields = await indexPatternsFetcher.getFieldsForWildcard({ | ||
pattern: logIndexBasePattern, | ||
}); | ||
|
||
return { | ||
fields, | ||
timeFieldName: '@timestamp', | ||
title: logIndexBasePattern, | ||
}; | ||
}, | ||
index: async (doc) => { | ||
const validation = fieldmapType.decode({ | ||
...doc, | ||
...defaults, | ||
}); | ||
|
||
if (isLeft(validation)) { | ||
throw createPathReporterError(validation); | ||
} | ||
|
||
const x = validation.right; | ||
|
||
const eventLog = await eventLogProvider.getLog(); | ||
const eventLogger = eventLog.getLogger('alerts'); | ||
eventLogger.logEvent(validation.right); | ||
}, | ||
bulkIndex: async (docs) => { | ||
const validations = docs.map((doc) => { | ||
return fieldmapType.decode({ | ||
...doc, | ||
...defaults, | ||
}); | ||
}); | ||
|
||
const validationErrors = compact( | ||
validations.map((validation) => | ||
isLeft(validation) ? createPathReporterError(validation) : null | ||
) | ||
); | ||
|
||
const validDocuments = compact( | ||
validations.map((validation) => (isRight(validation) ? validation.right : null)) | ||
); | ||
|
||
validationErrors.forEach((error) => { | ||
logger.error(error); | ||
}); | ||
|
||
const eventLog = await eventLogProvider.getLog(); | ||
const eventLogger = eventLog.getLogger('alerts'); | ||
|
||
validDocuments.forEach((doc) => { | ||
eventLogger.logEvent(doc); | ||
}); | ||
}, | ||
}; | ||
|
||
// @ts-expect-error: We can't use ScopedRuleRegistryClient<BaseRuleFieldMap> | ||
// when creating the client, due to #41693 which will be fixed in 4.2 | ||
return client; | ||
} |
57 changes: 57 additions & 0 deletions
57
...plugins/rule_registry/server/rule_registry_v2/create_scoped_rule_registry_client/types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* 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 { FieldDescriptor } from 'src/plugins/data/server'; | ||
import { ESSearchRequest, ESSearchResponse } from 'typings/elasticsearch'; | ||
import { | ||
PatternsUnionOf, | ||
PickWithPatterns, | ||
OutputOfFieldMap, | ||
BaseRuleFieldMap, | ||
} from '../../../common'; | ||
import { Event } from '../../event_log'; | ||
|
||
export type PrepopulatedRuleEventFields = keyof Pick< | ||
BaseRuleFieldMap, | ||
'rule.uuid' | 'rule.id' | 'rule.name' | 'rule.category' | 'kibana.rac.producer' | ||
>; | ||
|
||
type FieldsOf<TFieldMap extends BaseRuleFieldMap> = | ||
| Array<{ field: PatternsUnionOf<TFieldMap> } | PatternsUnionOf<TFieldMap>> | ||
| PatternsUnionOf<TFieldMap>; | ||
|
||
type Fields<TPattern extends string> = Array<{ field: TPattern } | TPattern> | TPattern; | ||
|
||
type FieldsESSearchRequest<TFieldMap extends BaseRuleFieldMap> = ESSearchRequest & { | ||
body?: { fields: FieldsOf<TFieldMap> }; | ||
}; | ||
|
||
export type EventsOf< | ||
TFieldsESSearchRequest extends ESSearchRequest, | ||
TFieldMap extends BaseRuleFieldMap | ||
> = TFieldsESSearchRequest extends { body: { fields: infer TFields } } | ||
? TFields extends Fields<infer TPattern> | ||
? Array<OutputOfFieldMap<PickWithPatterns<TFieldMap, TPattern[]>>> | ||
: never | ||
: never; | ||
|
||
export interface ScopedRuleRegistryClient<TFieldMap extends BaseRuleFieldMap> { | ||
search<TSearchRequest extends FieldsESSearchRequest<TFieldMap>>( | ||
request: TSearchRequest | ||
): Promise<{ | ||
body: ESSearchResponse<unknown, TSearchRequest>; | ||
events: EventsOf<TSearchRequest, TFieldMap>; | ||
}>; | ||
getDynamicIndexPattern(): Promise<{ | ||
title: string; | ||
timeFieldName: string; | ||
fields: FieldDescriptor[]; | ||
}>; | ||
|
||
index(event: Event<TFieldMap>): Promise<void>; | ||
bulkIndex(events: Array<Event<TFieldMap>>): Promise<void>; | ||
} |
9 changes: 9 additions & 0 deletions
9
x-pack/plugins/rule_registry/server/rule_registry_v2/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
export * from './public_api'; | ||
export * from './rule_registry'; |
Oops, something went wrong.