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

[RAM] [Flapping] Add Flapping Rules Settings #147774

Merged
merged 29 commits into from
Jan 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b254a69
Rules config initial implementation
JiaweiWu Dec 19, 2022
7a02e3a
Merge branch 'main' into issue-143529-flapping-config
kibanamachine Dec 19, 2022
429b8fa
Merge branch 'main' into issue-143529-flapping-config
JiaweiWu Dec 20, 2022
5711f5b
Future proof component with compressed mode
JiaweiWu Dec 21, 2022
546394c
Merge branch 'issue-143529-flapping-config' of https://github.com/Jia…
JiaweiWu Dec 21, 2022
5df9511
Merge branch 'main' into issue-143529-flapping-config
kibanamachine Dec 28, 2022
415132b
Address some comments
JiaweiWu Jan 3, 2023
b0b24ff
Merge branch 'main' into issue-143529-flapping-config
kibanamachine Jan 3, 2023
afa03cd
Unit testing
JiaweiWu Jan 4, 2023
62fdf8a
Merge branch 'main' into issue-143529-flapping-config
kibanamachine Jan 4, 2023
bbb52ad
Features integration tests
JiaweiWu Jan 5, 2023
339ceca
Merge branch 'main' into issue-143529-flapping-config
kibanamachine Jan 5, 2023
9c9f855
Merge branch 'main' into issue-143529-flapping-config
kibanamachine Jan 6, 2023
b40135e
API integration tests and E2E functional tests
JiaweiWu Jan 9, 2023
0061c4c
Merge branch 'main' into issue-143529-flapping-config
kibanamachine Jan 9, 2023
25dc7f0
Address comments
JiaweiWu Jan 10, 2023
f79af29
Merge branch 'main' into issue-143529-flapping-config
kibanamachine Jan 10, 2023
390570a
Fix SO migration snapshots and tests
JiaweiWu Jan 10, 2023
49627f8
Merge branch 'main' into issue-143529-flapping-config
kibanamachine Jan 10, 2023
2cc12bd
Merge branch 'main' into issue-143529-flapping-config
kibanamachine Jan 10, 2023
3a2095a
Merge branch 'main' into issue-143529-flapping-config
kibanamachine Jan 10, 2023
a9e8af9
Fix merge conflict
JiaweiWu Jan 10, 2023
c19227a
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Jan 10, 2023
64f01cf
Address comments
JiaweiWu Jan 12, 2023
6b1ae68
Address comments
JiaweiWu Jan 15, 2023
efd1b47
Fix merge conflict
JiaweiWu Jan 15, 2023
585a40f
Fix tests
JiaweiWu Jan 16, 2023
1f63344
Merge branch 'main' into issue-143529-flapping-config
kibanamachine Jan 17, 2023
3c54c9a
Remove placeholder slider tooltip
JiaweiWu Jan 17, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
"osquery-pack-asset": "de8783298eb33a577bf1fa0caacd42121dcfae91",
"osquery-saved-query": "7b213b4b7a3e59350e99c50e8df9948662ed493a",
"query": "4640ef356321500a678869f24117b7091a911cb6",
"rules-settings": "1af4c9abd4b40a154e233c2af4867df7aab7ac24",
"sample-data-telemetry": "8b10336d9efae6f3d5593c4cc89fb4abcdf84e04",
"search": "c48f5ab5d94545780ea98de1bff9e39f17f3606b",
"search-session": "ba383309da68a15be3765977f7a44c84f0ec7964",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ const previouslyRegisteredTypes = [
'osquery-usage-metric',
'osquery-manager-usage-metric',
'query',
'rules-settings',
'sample-data-telemetry',
'search',
'search-session',
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/alerting/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import { AlertsHealth } from './rule';

export * from './rule';
export * from './rules_settings';
export * from './rule_type';
export * from './rule_task_instance';
export * from './rule_navigation';
Expand Down
51 changes: 51 additions & 0 deletions x-pack/plugins/alerting/common/rules_settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 interface RulesSettingsModificationMetadata {
createdBy: string | null;
updatedBy: string | null;
createdAt: string;
updatedAt: string;
}

export interface RulesSettingsFlappingProperties {
enabled: boolean;
lookBackWindow: number;
statusChangeThreshold: number;
}

export type RulesSettingsFlapping = RulesSettingsFlappingProperties &
RulesSettingsModificationMetadata;

export interface RulesSettings {
flapping: RulesSettingsFlapping;
}

export const MIN_LOOK_BACK_WINDOW = 2;
export const MAX_LOOK_BACK_WINDOW = 20;
export const MIN_STATUS_CHANGE_THRESHOLD = 2;
export const MAX_STATUS_CHANGE_THRESHOLD = 20;

export const RULES_SETTINGS_FEATURE_ID = 'rulesSettings';
export const ALL_FLAPPING_SETTINGS_SUB_FEATURE_ID = 'allFlappingSettings';
export const READ_FLAPPING_SETTINGS_SUB_FEATURE_ID = 'readFlappingSettings';

export const API_PRIVILEGES = {
READ_FLAPPING_SETTINGS: 'read-flapping-settings',
WRITE_FLAPPING_SETTINGS: 'write-flapping-settings',
};

export const RULES_SETTINGS_SAVED_OBJECT_TYPE = 'rules-settings';
export const RULES_SETTINGS_SAVED_OBJECT_ID = 'rules-settings';

export const DEFAULT_LOOK_BACK_WINDOW = 20;
export const DEFAULT_STATUS_CHANGE_THRESHOLD = 4;

export const DEFAULT_FLAPPING_SETTINGS = {
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 couldn't find the existing default values, so this will obviously need to be changed 🙂

Copy link
Contributor

Choose a reason for hiding this comment

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

enabled: true,
lookBackWindow: 20,
statusChangeThreshold: 4,
};
4 changes: 4 additions & 0 deletions x-pack/plugins/alerting/server/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ describe('Alerting Plugin', () => {
statusService: statusServiceMock.createSetupContract(),
monitoringCollection: monitoringCollectionMock.createSetup(),
data: dataPluginMock.createSetupContract() as unknown as DataPluginSetup,
features: featuresPluginMock.createSetup(),
};

let plugin: AlertingPlugin;
Expand Down Expand Up @@ -221,6 +222,7 @@ describe('Alerting Plugin', () => {
statusService: statusServiceMock.createSetupContract(),
monitoringCollection: monitoringCollectionMock.createSetup(),
data: dataPluginMock.createSetupContract() as unknown as DataPluginSetup,
features: featuresPluginMock.createSetup(),
});

const startContract = plugin.start(coreMock.createStart(), {
Expand Down Expand Up @@ -267,6 +269,7 @@ describe('Alerting Plugin', () => {
statusService: statusServiceMock.createSetupContract(),
monitoringCollection: monitoringCollectionMock.createSetup(),
data: dataPluginMock.createSetupContract() as unknown as DataPluginSetup,
features: featuresPluginMock.createSetup(),
});

const startContract = plugin.start(coreMock.createStart(), {
Expand Down Expand Up @@ -324,6 +327,7 @@ describe('Alerting Plugin', () => {
statusService: statusServiceMock.createSetupContract(),
monitoringCollection: monitoringCollectionMock.createSetup(),
data: dataPluginMock.createSetupContract() as unknown as DataPluginSetup,
features: featuresPluginMock.createSetup(),
});

const startContract = plugin.start(coreMock.createStart(), {
Expand Down
24 changes: 22 additions & 2 deletions x-pack/plugins/alerting/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,17 @@ import {
IEventLogService,
IEventLogClientService,
} from '@kbn/event-log-plugin/server';
import { PluginStartContract as FeaturesPluginStart } from '@kbn/features-plugin/server';
import {
PluginStartContract as FeaturesPluginStart,
PluginSetupContract as FeaturesPluginSetup,
} from '@kbn/features-plugin/server';
import { PluginStart as DataPluginStart } from '@kbn/data-plugin/server';
import { MonitoringCollectionSetup } from '@kbn/monitoring-collection-plugin/server';
import { SharePluginStart } from '@kbn/share-plugin/server';
import { RuleTypeRegistry } from './rule_type_registry';
import { TaskRunnerFactory } from './task_runner';
import { RulesClientFactory } from './rules_client_factory';
import { RulesSettingsClientFactory } from './rules_settings_client_factory';
import { ILicenseState, LicenseState } from './lib/license_state';
import { AlertingRequestHandlerContext, ALERTS_FEATURE_ID } from './types';
import { defineRoutes } from './routes';
Expand Down Expand Up @@ -82,6 +86,7 @@ import { getSecurityHealth, SecurityHealth } from './lib/get_security_health';
import { registerNodeCollector, registerClusterCollector, InMemoryMetrics } from './monitoring';
import { getRuleTaskTimeout } from './lib/get_rule_task_timeout';
import { getActionsConfigMap } from './lib/get_actions_config_map';
import { rulesSettingsFeature } from './rules_settings_feature';

export const EVENT_LOG_PROVIDER = 'alerting';
export const EVENT_LOG_ACTIONS = {
Expand Down Expand Up @@ -146,6 +151,7 @@ export interface AlertingPluginsSetup {
statusService: StatusServiceSetup;
monitoringCollection: MonitoringCollectionSetup;
data: DataPluginSetup;
features: FeaturesPluginSetup;
}

export interface AlertingPluginsStart {
Expand All @@ -172,6 +178,7 @@ export class AlertingPlugin {
private security?: SecurityPluginSetup;
private readonly rulesClientFactory: RulesClientFactory;
private readonly alertingAuthorizationClientFactory: AlertingAuthorizationClientFactory;
private readonly rulesSettingsClientFactory: RulesSettingsClientFactory;
private readonly telemetryLogger: Logger;
private readonly kibanaVersion: PluginInitializerContext['env']['packageInfo']['version'];
private eventLogService?: IEventLogService;
Expand All @@ -186,6 +193,7 @@ export class AlertingPlugin {
this.taskRunnerFactory = new TaskRunnerFactory();
this.rulesClientFactory = new RulesClientFactory();
this.alertingAuthorizationClientFactory = new AlertingAuthorizationClientFactory();
this.rulesSettingsClientFactory = new RulesSettingsClientFactory();
this.telemetryLogger = initializerContext.logger.get('usage');
this.kibanaVersion = initializerContext.env.packageInfo.version;
this.inMemoryMetrics = new InMemoryMetrics(initializerContext.logger.get('in_memory_metrics'));
Expand All @@ -210,6 +218,8 @@ export class AlertingPlugin {
};
});

plugins.features.registerKibanaFeature(rulesSettingsFeature);

this.isESOCanEncrypt = plugins.encryptedSavedObjects.canEncrypt;

if (!this.isESOCanEncrypt) {
Expand Down Expand Up @@ -368,6 +378,7 @@ export class AlertingPlugin {
ruleTypeRegistry,
rulesClientFactory,
alertingAuthorizationClientFactory,
rulesSettingsClientFactory,
security,
licenseState,
} = this;
Expand Down Expand Up @@ -416,6 +427,12 @@ export class AlertingPlugin {
minimumScheduleInterval: this.config.rules.minimumScheduleInterval,
});

rulesSettingsClientFactory.initialize({
logger: this.logger,
savedObjectsService: core.savedObjects,
securityPluginStart: plugins.security,
});

const getRulesClientWithRequest = (request: KibanaRequest) => {
if (isESOCanEncrypt !== true) {
throw new Error(
Expand Down Expand Up @@ -483,13 +500,16 @@ export class AlertingPlugin {
private createRouteHandlerContext = (
core: CoreSetup<AlertingPluginsStart, unknown>
): IContextProvider<AlertingRequestHandlerContext, 'alerting'> => {
const { ruleTypeRegistry, rulesClientFactory } = this;
const { ruleTypeRegistry, rulesClientFactory, rulesSettingsClientFactory } = this;
return async function alertsRouteHandlerContext(context, request) {
const [{ savedObjects }] = await core.getStartServices();
return {
getRulesClient: () => {
return rulesClientFactory!.create(request, savedObjects);
},
getRulesSettingsClient: () => {
return rulesSettingsClientFactory.createWithAuthorization(request);
},
listTypes: ruleTypeRegistry!.list.bind(ruleTypeRegistry!),
getFrameworkHealth: async () =>
await getHealth(savedObjects.createInternalRepository(['alert'])),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@ import { identity } from 'lodash';
import type { MethodKeysOf } from '@kbn/utility-types';
import { httpServerMock } from '@kbn/core/server/mocks';
import { rulesClientMock, RulesClientMock } from '../rules_client.mock';
import { rulesSettingsClientMock, RulesSettingsClientMock } from '../rules_settings_client.mock';
import { AlertsHealth, RuleType } from '../../common';
import type { AlertingRequestHandlerContext } from '../types';

export function mockHandlerArguments(
{
rulesClient = rulesClientMock.create(),
rulesSettingsClient = rulesSettingsClientMock.create(),
listTypes: listTypesRes = [],
getFrameworkHealth,
areApiKeysEnabled,
}: {
rulesClient?: RulesClientMock;
rulesSettingsClient?: RulesSettingsClientMock;
listTypes?: RuleType[];
getFrameworkHealth?: jest.MockInstance<Promise<AlertsHealth>, []> &
(() => Promise<AlertsHealth>);
Expand All @@ -41,6 +44,9 @@ export function mockHandlerArguments(
getRulesClient() {
return rulesClient || rulesClientMock.create();
},
getRulesSettingsClient() {
return rulesSettingsClient || rulesSettingsClientMock.create();
},
getFrameworkHealth,
areApiKeysEnabled: areApiKeysEnabled ? areApiKeysEnabled : () => Promise.resolve(true),
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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 { httpServiceMock } from '@kbn/core/server/mocks';
import { licenseStateMock } from '../lib/license_state.mock';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { rulesSettingsClientMock, RulesSettingsClientMock } from '../rules_settings_client.mock';
import { getFlappingSettingsRoute } from './get_flapping_settings';

let rulesSettingsClient: RulesSettingsClientMock;

jest.mock('../lib/license_api_access', () => ({
verifyApiAccess: jest.fn(),
}));

beforeEach(() => {
jest.resetAllMocks();
rulesSettingsClient = rulesSettingsClientMock.create();
});

describe('getFlappingSettingsRoute', () => {
test('gets flapping settings', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();

getFlappingSettingsRoute(router, licenseState);

const [config, handler] = router.get.mock.calls[0];

expect(config).toMatchInlineSnapshot(`
Object {
"options": Object {
"tags": Array [
"access:read-flapping-settings",
],
},
"path": "/internal/alerting/rules/settings/_flapping",
"validate": false,
}
`);

(rulesSettingsClient.flapping().get as jest.Mock).mockResolvedValue({
enabled: true,
lookBackWindow: 10,
statusChangeThreshold: 10,
createdBy: 'test name',
updatedBy: 'test name',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});

const [context, req, res] = mockHandlerArguments({ rulesSettingsClient }, {}, ['ok']);

await handler(context, req, res);

expect(rulesSettingsClient.flapping().get).toHaveBeenCalledTimes(1);
expect(res.ok).toHaveBeenCalled();
});
});
34 changes: 34 additions & 0 deletions x-pack/plugins/alerting/server/routes/get_flapping_settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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 { IRouter } from '@kbn/core/server';
import { ILicenseState } from '../lib';
import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../types';
import { verifyAccessAndContext } from './lib';
import { API_PRIVILEGES } from '../../common';

export const getFlappingSettingsRoute = (
router: IRouter<AlertingRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.get(
{
path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/settings/_flapping`,
XavierM marked this conversation as resolved.
Show resolved Hide resolved
validate: false,
options: {
tags: [`access:${API_PRIVILEGES.READ_FLAPPING_SETTINGS}`],
},
},
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const rulesSettingsClient = (await context.alerting).getRulesSettingsClient();
const flappingSettings = await rulesSettingsClient.flapping().get();
return res.ok({ body: flappingSettings });
})
)
);
};
4 changes: 4 additions & 0 deletions x-pack/plugins/alerting/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import { bulkDeleteRulesRoute } from './bulk_delete_rules';
import { bulkEnableRulesRoute } from './bulk_enable_rules';
import { bulkDisableRulesRoute } from './bulk_disable_rules';
import { cloneRuleRoute } from './clone_rule';
import { getFlappingSettingsRoute } from './get_flapping_settings';
import { updateFlappingSettingsRoute } from './update_flapping_settings';

export interface RouteOptions {
router: IRouter<AlertingRequestHandlerContext>;
Expand Down Expand Up @@ -87,4 +89,6 @@ export function defineRoutes(opts: RouteOptions) {
unsnoozeRuleRoute(router, licenseState);
runSoonRoute(router, licenseState);
cloneRuleRoute(router, licenseState);
getFlappingSettingsRoute(router, licenseState);
updateFlappingSettingsRoute(router, licenseState);
}
Loading