Skip to content

Commit

Permalink
Updates namings & deprecations, enabled migration, and added support …
Browse files Browse the repository at this point in the history
…to references for create API
  • Loading branch information
spong committed Oct 13, 2021
1 parent bbb7fc3 commit bd30df7
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 130 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { updatePrepackagedRules } from '../../rules/update_prepacked_rules';
import { getRulesToInstall } from '../../rules/get_rules_to_install';
import { getRulesToUpdate } from '../../rules/get_rules_to_update';
import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackaged_rules';
import { ruleAssetSavedObjectsClientFactory } from '../../rules/rule_asset_saved_objects_client';
import { ruleAssetSavedObjectsClientFactory } from '../../rules/rule_asset/rule_asset_saved_objects_client';

import { buildSiemResponse } from '../utils';
import { RulesClient } from '../../../../../../alerting/server';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { getRulesToUpdate } from '../../rules/get_rules_to_update';
import { findRules } from '../../rules/find_rules';
import { getLatestPrepackagedRules } from '../../rules/get_prepackaged_rules';
import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackaged_rules';
import { ruleAssetSavedObjectsClientFactory } from '../../rules/rule_asset_saved_objects_client';
import { ruleAssetSavedObjectsClientFactory } from '../../rules/rule_asset/rule_asset_saved_objects_client';
import { buildFrameworkRequest } from '../../../timeline/utils/common';
import { ConfigType } from '../../../../config';
import { SetupPlugins } from '../../../../plugin';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,21 @@ import {
SavedObjectsFindOptions,
SavedObjectsFindResult,
SavedObjectsFindOptionsReference,
} from '../../../../../../../../src/core/server';
SavedObjectsCreateOptions,
} from 'kibana/server';
// eslint-disable-next-line no-restricted-imports
import { ruleStatusSavedObjectType } from '../../rules/legacy_rule_status/legacy_rule_status_saved_object_mappings';
import { legacyRuleStatusSavedObjectType } from '../../rules/legacy_rule_status/legacy_rule_status_saved_object_mappings';
import { IRuleStatusSOAttributes } from '../../rules/types';

export interface RuleStatusSavedObjectsClient {
find: (
options?: Omit<SavedObjectsFindOptions, 'type'>
) => Promise<Array<SavedObjectsFindResult<IRuleStatusSOAttributes>>>;
findBulk: (ids: string[], statusesPerId: number) => Promise<FindBulkResponse>;
create: (attributes: IRuleStatusSOAttributes) => Promise<SavedObject<IRuleStatusSOAttributes>>;
create: (
attributes: IRuleStatusSOAttributes,
options?: SavedObjectsCreateOptions
) => Promise<SavedObject<IRuleStatusSOAttributes>>;
update: (
id: string,
attributes: Partial<IRuleStatusSOAttributes>
Expand All @@ -44,15 +48,14 @@ export const ruleStatusSavedObjectsClientFactory = (
find: async (options) => {
const result = await savedObjectsClient.find<IRuleStatusSOAttributes>({
...options,
type: ruleStatusSavedObjectType,
type: legacyRuleStatusSavedObjectType,
});
return result.saved_objects;
},
findBulk: async (ids, statusesPerId) => {
if (ids.length === 0) {
return {};
}
const filter = `${ruleStatusSavedObjectType}.references.type: "alert"`;
const references = ids.map<SavedObjectsFindOptionsReference>((alertId) => ({
id: alertId,
type: 'alert',
Expand All @@ -61,15 +64,15 @@ export const ruleStatusSavedObjectsClientFactory = (
const aggs = {
alertIds: {
terms: {
field: `${ruleStatusSavedObjectType}.references.id`,
field: `${legacyRuleStatusSavedObjectType}.references.id`,
size: ids.length,
},
aggs: {
most_recent_statuses: {
top_hits: {
sort: [
{
[`${ruleStatusSavedObjectType}.statusDate`]: {
[`${legacyRuleStatusSavedObjectType}.statusDate`]: {
order,
},
},
Expand All @@ -81,10 +84,9 @@ export const ruleStatusSavedObjectsClientFactory = (
},
};
const results = await savedObjectsClient.find({
filter,
hasReference: references,
aggs,
type: ruleStatusSavedObjectType,
type: legacyRuleStatusSavedObjectType,
perPage: 0,
});
const buckets = get(results, 'aggregations.alertIds.buckets');
Expand All @@ -97,7 +99,10 @@ export const ruleStatusSavedObjectsClientFactory = (
return acc;
}, {});
},
create: (attributes) => savedObjectsClient.create(ruleStatusSavedObjectType, attributes),
update: (id, attributes) => savedObjectsClient.update(ruleStatusSavedObjectType, id, attributes),
delete: (id) => savedObjectsClient.delete(ruleStatusSavedObjectType, id),
create: (attributes, options) => {
return savedObjectsClient.create(legacyRuleStatusSavedObjectType, attributes, options);
},
update: (id, attributes) =>
savedObjectsClient.update(legacyRuleStatusSavedObjectType, id, attributes),
delete: (id) => savedObjectsClient.delete(legacyRuleStatusSavedObjectType, id),
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
* 2.0.
*/

import { SavedObject } from 'src/core/server';
import { SavedObject, SavedObjectReference } from 'src/core/server';
import { SavedObjectsClientContract } from '../../../../../../../../src/core/server';
import { RuleExecutionStatus } from '../../../../../common/detection_engine/schemas/common/schemas';
// eslint-disable-next-line no-restricted-imports
import { legacyGetRuleReference } from '../../rules/legacy_rule_status/legacy_utils';

import { IRuleStatusSOAttributes } from '../../rules/types';
import {
RuleStatusSavedObjectsClient,
Expand Down Expand Up @@ -79,20 +82,24 @@ export class SavedObjectsAdapter implements IRuleExecutionLogClient {
private createNewRuleStatus = async (
ruleId: string
): Promise<SavedObject<IRuleStatusSOAttributes>> => {
const references: SavedObjectReference[] = [legacyGetRuleReference(ruleId)];
const now = new Date().toISOString();
return this.ruleStatusClient.create({
alertId: ruleId,
statusDate: now,
status: RuleExecutionStatus['going to run'],
lastFailureAt: null,
lastSuccessAt: null,
lastFailureMessage: null,
lastSuccessMessage: null,
gap: null,
bulkCreateTimeDurations: [],
searchAfterTimeDurations: [],
lastLookBackDate: null,
});
return this.ruleStatusClient.create(
{
alertId: ruleId,
statusDate: now,
status: RuleExecutionStatus['going to run'],
lastFailureAt: null,
lastSuccessAt: null,
lastFailureMessage: null,
lastSuccessMessage: null,
gap: null,
bulkCreateTimeDurations: [],
searchAfterTimeDurations: [],
lastLookBackDate: null,
},
{ references }
);
};

private getOrCreateRuleStatuses = async (
Expand Down Expand Up @@ -138,7 +145,10 @@ export class SavedObjectsAdapter implements IRuleExecutionLogClient {

// We always update the newest status, so to 'persist' a failure we push a copy to the head of the list
await this.ruleStatusClient.update(currentStatus.id, failureAttributes);
const lastStatus = await this.ruleStatusClient.create(failureAttributes);
const references: SavedObjectReference[] = [
legacyGetRuleReference(failureAttributes.alertId),
];
const lastStatus = await this.ruleStatusClient.create(failureAttributes, { references });

// drop oldest failures
const oldStatuses = [lastStatus, ...ruleStatuses].slice(MAX_RULE_STATUSES);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {

// TODO: convert rules files to TS and add explicit type definitions
import { rawRules } from './prepackaged_rules';
import { RuleAssetSavedObjectsClient } from './rule_asset_saved_objects_client';
import { RuleAssetSavedObjectsClient } from './rule_asset/rule_asset_saved_objects_client';
import { IRuleAssetSOAttributes } from './types';
import { SavedObjectAttributes } from '../../../../../../../src/core/types';
import { ConfigType } from '../../../config';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* 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 {
SavedObjectReference,
SavedObjectSanitizedDoc,
SavedObjectUnsanitizedDoc,
} from 'kibana/server';
import { isString } from 'lodash/fp';
import { IRuleSavedAttributesSavedObjectAttributes } from '../types';
// eslint-disable-next-line no-restricted-imports
import { legacyGetRuleReference } from './legacy_utils';

/**
* This side-car rule status SO is deprecated and is to be replaced by the RuleExecutionLog on Event-Log and
* additional fields on the Alerting Framework Rule SO.
*
* @deprecated Remove this once we've fully migrated to event-log and no longer require addition status SO (8.x)
*/
export const legacyRuleStatusSavedObjectMigration = {
'7.16.0': (
doc: SavedObjectUnsanitizedDoc<IRuleSavedAttributesSavedObjectAttributes>
): SavedObjectSanitizedDoc<IRuleSavedAttributesSavedObjectAttributes> => {
return legacyMigrateRuleAlertIdSOReferences(doc);
},
};

/**
* This migrates alertId within legacy `siem-detection-engine-rule-status` to saved object references on an upgrade.
* We only migrate alertId if we find these conditions:
* - alertId is a string and not null, undefined, or malformed data.
* - The existing references do not already have a alertId found within it.
*
* Some of these issues could crop up during either user manual errors of modifying things, earlier migration
* issues, etc... so we are safer to check them as possibilities
*
* @deprecated Remove this once we've fully migrated to event-log and no longer require addition status SO (8.x)
* @param doc The document having an alertId to migrate into references
* @returns The document migrated with saved object references
*/
export const legacyMigrateRuleAlertIdSOReferences = (
doc: SavedObjectUnsanitizedDoc<IRuleSavedAttributesSavedObjectAttributes>
): SavedObjectSanitizedDoc<IRuleSavedAttributesSavedObjectAttributes> => {
const { references } = doc;

// Isolate alertId from the doc
const { alertId, ...attributesWithoutAlertId } = doc.attributes;
const existingReferences = references ?? [];

if (!isString(alertId)) {
// early return if alertId is not a string as expected
return { ...doc, references: existingReferences };
} else {
const alertReferences = legacyMigrateAlertId({
alertId,
existingReferences,
});

return {
...doc,
attributes: {
...attributesWithoutAlertId.attributes,
},
references: [...existingReferences, ...alertReferences],
};
}
};

/**
* This is a helper to migrate "alertId"
*
* @deprecated Remove this once we've fully migrated to event-log and no longer require addition status SO (8.x)
*
* @param existingReferences The existing saved object references
* @param alertId The alertId to migrate
*
* @returns The savedObjectReferences migrated
*/
export const legacyMigrateAlertId = ({
existingReferences,
alertId,
}: {
existingReferences: SavedObjectReference[];
alertId: string;
}): SavedObjectReference[] => {
const existingReferenceFound = existingReferences.find((reference) => {
return reference.id === alertId && reference.type === 'alert';
});
if (existingReferenceFound) {
return [];
} else {
return [legacyGetRuleReference(alertId)];
}
};
Loading

0 comments on commit bd30df7

Please sign in to comment.