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

Implemented Alerting health status pusher by using task manager and status pooler for Kibana status plugins 'kibanahost/api/status' #79056

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
27149da
Implemented Alerting health status pusher by using task manager and s…
YulNaumenko Oct 1, 2020
f5eedb2
Merge remote-tracking branch 'upstream/master' into alerting-framewor…
YulNaumenko Oct 1, 2020
36346a0
Exposed health task registration to alerts plugin
YulNaumenko Oct 1, 2020
5231836
Merge remote-tracking branch 'upstream/master' into alerting-framewor…
YulNaumenko Oct 2, 2020
ef4c938
Fixed type error
YulNaumenko Oct 2, 2020
e229a0e
Merge remote-tracking branch 'upstream/master' into alerting-framewor…
YulNaumenko Oct 6, 2020
ff59fce
Extended health API endpoint with info about decryption failures, add…
YulNaumenko Oct 6, 2020
c7d8b14
adjusted query
YulNaumenko Oct 6, 2020
e36516b
Merge remote-tracking branch 'upstream/master' into alerting-framewor…
YulNaumenko Oct 6, 2020
c0d96b3
Tested locally and got it working as expected, fixed tests and type c…
YulNaumenko Oct 7, 2020
3f52662
Added unit tests
YulNaumenko Oct 7, 2020
ae20ae3
Merge remote-tracking branch 'upstream/master' into alerting-framewor…
YulNaumenko Oct 8, 2020
35eba5e
Changed AlertExecutionStatusErrorReasons to be enum
YulNaumenko Oct 8, 2020
f20a0e0
Uppercase the enum
YulNaumenko Oct 8, 2020
bde12d8
Replaced string values to enum
YulNaumenko Oct 8, 2020
ea01e31
Fixed types
YulNaumenko Oct 8, 2020
db2259e
Extended AlertsClient with getHealth method
YulNaumenko Oct 9, 2020
308dae2
added return type to healthStatus$
YulNaumenko Oct 9, 2020
9584698
Merge remote-tracking branch 'upstream/master' into alerting-framewor…
YulNaumenko Oct 19, 2020
315bd16
Merge remote-tracking branch 'upstream/master' into alerting-framewor…
YulNaumenko Oct 29, 2020
3ef7cd5
Added configurable health check interval and timestamps
YulNaumenko Oct 29, 2020
aab82ac
Extended update core status interval to 5mins
YulNaumenko Oct 29, 2020
b337960
Fixed failing tests
YulNaumenko Oct 29, 2020
c3ed074
Registered alerts config
YulNaumenko Oct 29, 2020
11af177
Fixed date for ok health state
YulNaumenko Oct 29, 2020
d418ddb
fixed jest test
YulNaumenko Oct 29, 2020
0aeaf5d
fixed task state
YulNaumenko Oct 31, 2020
c3ae044
Merge remote-tracking branch 'upstream/master' into alerting-framewor…
YulNaumenko Nov 2, 2020
41e338a
Merge remote-tracking branch 'upstream/master' into alerting-framewor…
YulNaumenko Nov 4, 2020
397a622
Fixed due to comments, moved getHealth to a plugin level
YulNaumenko Nov 4, 2020
b344aeb
fixed type checks
YulNaumenko Nov 4, 2020
5197255
Merge remote-tracking branch 'upstream/master' into alerting-framewor…
YulNaumenko Nov 6, 2020
8964d24
Added sorting to the latest Ok state last update
YulNaumenko Nov 6, 2020
75d7eea
adjusted error queries
YulNaumenko Nov 6, 2020
169e946
Fixed jest tests
YulNaumenko Nov 6, 2020
e23eb26
removed unused
YulNaumenko Nov 6, 2020
bc88bdf
fixed type check
YulNaumenko Nov 6, 2020
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
34 changes: 27 additions & 7 deletions x-pack/plugins/alerts/common/alert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@ export interface IntervalSchedule extends SavedObjectAttributes {
export const AlertExecutionStatusValues = ['ok', 'active', 'error', 'pending', 'unknown'] as const;
export type AlertExecutionStatuses = typeof AlertExecutionStatusValues[number];

export const AlertExecutionStatusErrorReasonValues = [
'read',
'decrypt',
'execute',
'unknown',
] as const;
export type AlertExecutionStatusErrorReasons = typeof AlertExecutionStatusErrorReasonValues[number];
export enum AlertExecutionStatusErrorReasons {
Read = 'read',
Decrypt = 'decrypt',
Execute = 'execute',
Unknown = 'unknown',
}

export interface AlertExecutionStatus {
status: AlertExecutionStatuses;
Expand Down Expand Up @@ -74,3 +73,24 @@ export interface Alert {
}

export type SanitizedAlert = Omit<Alert, 'apiKey'>;

export enum HealthStatus {
OK = 'ok',
Warning = 'warn',
Error = 'error',
}

export interface AlertsHealth {
decryptionHealth: {
status: HealthStatus;
timestamp: string;
};
executionHealth: {
status: HealthStatus;
timestamp: string;
};
readHealth: {
status: HealthStatus;
timestamp: string;
};
}
3 changes: 3 additions & 0 deletions x-pack/plugins/alerts/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { AlertsHealth } from './alert';

export * from './alert';
export * from './alert_type';
export * from './alert_instance';
Expand All @@ -19,6 +21,7 @@ export interface ActionGroup {
export interface AlertingFrameworkHealth {
YulNaumenko marked this conversation as resolved.
Show resolved Hide resolved
isSufficientlySecure: boolean;
hasPermanentEncryptionKey: boolean;
alertingFrameworkHeath: AlertsHealth;
}

export const BASE_ALERT_API_PATH = '/api/alerts';
Expand Down
19 changes: 19 additions & 0 deletions x-pack/plugins/alerts/server/config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { configSchema } from './config';

describe('config validation', () => {
test('alerts defaults', () => {
const config: Record<string, unknown> = {};
expect(configSchema.validate(config)).toMatchInlineSnapshot(`
Object {
"healthCheck": Object {
"interval": "60m",
},
}
`);
});
});
16 changes: 16 additions & 0 deletions x-pack/plugins/alerts/server/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { schema, TypeOf } from '@kbn/config-schema';
import { validateDurationSchema } from './lib';

export const configSchema = schema.object({
healthCheck: schema.object({
interval: schema.string({ validate: validateDurationSchema, defaultValue: '60m' }),
}),
});

export type AlertsConfig = TypeOf<typeof configSchema>;
221 changes: 221 additions & 0 deletions x-pack/plugins/alerts/server/health/get_health.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { savedObjectsRepositoryMock } from '../../../../../src/core/server/mocks';
import { AlertExecutionStatusErrorReasons, HealthStatus } from '../types';
import { getHealth } from './get_health';

const savedObjectsRepository = savedObjectsRepositoryMock.create();

describe('getHealth()', () => {
test('return true if some of alerts has a decryption error', async () => {
const lastExecutionDateError = new Date().toISOString();
const lastExecutionDate = new Date().toISOString();
savedObjectsRepository.find.mockResolvedValueOnce({
total: 1,
per_page: 1,
page: 1,
saved_objects: [
{
id: '1',
type: 'alert',
attributes: {
alertTypeId: 'myType',
schedule: { interval: '10s' },
params: {
bar: true,
},
createdAt: new Date().toISOString(),
actions: [
{
group: 'default',
actionRef: 'action_0',
params: {
foo: true,
},
},
],
executionStatus: {
status: 'error',
lastExecutionDate: lastExecutionDateError,
error: {
reason: AlertExecutionStatusErrorReasons.Decrypt,
message: 'Failed decrypt',
},
},
},
score: 1,
references: [
{
name: 'action_0',
type: 'action',
id: '1',
},
],
},
],
});
savedObjectsRepository.find.mockResolvedValueOnce({
total: 0,
per_page: 10,
page: 1,
saved_objects: [],
});

savedObjectsRepository.find.mockResolvedValueOnce({
total: 0,
per_page: 10,
page: 1,
saved_objects: [],
});

savedObjectsRepository.find.mockResolvedValueOnce({
total: 1,
per_page: 1,
page: 1,
saved_objects: [
{
id: '2',
type: 'alert',
attributes: {
alertTypeId: 'myType',
schedule: { interval: '1s' },
params: {
bar: true,
},
createdAt: new Date().toISOString(),
actions: [],
executionStatus: {
status: 'ok',
lastExecutionDate,
},
},
score: 1,
references: [],
},
],
});
const result = await getHealth(savedObjectsRepository);
expect(result).toStrictEqual({
executionHealth: {
status: HealthStatus.OK,
timestamp: lastExecutionDate,
},
readHealth: {
status: HealthStatus.OK,
timestamp: lastExecutionDate,
},
decryptionHealth: {
status: HealthStatus.Warning,
timestamp: lastExecutionDateError,
},
});
expect(savedObjectsRepository.find).toHaveBeenCalledTimes(4);
});

test('return false if no alerts with a decryption error', async () => {
const lastExecutionDateError = new Date().toISOString();
const lastExecutionDate = new Date().toISOString();
savedObjectsRepository.find.mockResolvedValueOnce({
total: 0,
per_page: 10,
page: 1,
saved_objects: [],
});

savedObjectsRepository.find.mockResolvedValueOnce({
total: 1,
per_page: 1,
page: 1,
saved_objects: [
{
id: '1',
type: 'alert',
attributes: {
alertTypeId: 'myType',
schedule: { interval: '10s' },
params: {
bar: true,
},
createdAt: new Date().toISOString(),
actions: [
{
group: 'default',
actionRef: 'action_0',
params: {
foo: true,
},
},
],
executionStatus: {
status: 'error',
lastExecutionDate: lastExecutionDateError,
error: {
reason: AlertExecutionStatusErrorReasons.Execute,
message: 'Failed',
},
},
},
score: 1,
references: [
{
name: 'action_0',
type: 'action',
id: '1',
},
],
},
],
});
savedObjectsRepository.find.mockResolvedValueOnce({
total: 0,
per_page: 10,
page: 1,
saved_objects: [],
});

savedObjectsRepository.find.mockResolvedValueOnce({
total: 1,
per_page: 1,
page: 1,
saved_objects: [
{
id: '2',
type: 'alert',
attributes: {
alertTypeId: 'myType',
schedule: { interval: '1s' },
params: {
bar: true,
},
createdAt: new Date().toISOString(),
actions: [],
executionStatus: {
status: 'ok',
lastExecutionDate,
},
},
score: 1,
references: [],
},
],
});
const result = await getHealth(savedObjectsRepository);
expect(result).toStrictEqual({
executionHealth: {
status: HealthStatus.Warning,
timestamp: lastExecutionDateError,
},
readHealth: {
status: HealthStatus.OK,
timestamp: lastExecutionDate,
},
decryptionHealth: {
status: HealthStatus.OK,
timestamp: lastExecutionDate,
},
});
});
});
Loading