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 11 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
1 change: 1 addition & 0 deletions x-pack/plugins/alerts/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface ActionGroup {
export interface AlertingFrameworkHealth {
YulNaumenko marked this conversation as resolved.
Show resolved Hide resolved
isSufficientlySecure: boolean;
hasPermanentEncryptionKey: boolean;
hasDecryptionFailures: boolean;
}

export const BASE_ALERT_API_PATH = '/api/alerts';
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/alerts/server/alerts_client.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const createAlertsClientMock = () => {
unmuteInstance: jest.fn(),
listAlertTypes: jest.fn(),
getAlertInstanceSummary: jest.fn(),
hasDecryptionFailures: jest.fn(),
};
return mocked;
};
Expand Down
155 changes: 155 additions & 0 deletions x-pack/plugins/alerts/server/alerts_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2931,6 +2931,161 @@ describe('find()', () => {
});
});

describe('hasDecryptionFailures()', () => {
const listedTypes = new Set([
{
actionGroups: [],
actionVariables: undefined,
defaultActionGroupId: 'default',
id: 'myType',
name: 'myType',
producer: 'myApp',
},
]);
beforeAll(() => {
alertTypeRegistry.list.mockReturnValue(listedTypes);
});

test('return true if some of alerts has a decryption error', async () => {
unsecuredSavedObjectsClient.find.mockResolvedValueOnce({
total: 2,
per_page: 10,
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: new Date().toISOString(),
error: {
reason: 'decrypt',
message: 'Failed decrypt',
},
},
},
score: 1,
references: [
{
name: 'action_0',
type: 'action',
id: '1',
},
],
},
{
id: '2',
type: 'alert',
attributes: {
alertTypeId: 'myType',
schedule: { interval: '1s' },
params: {
bar: true,
},
createdAt: new Date().toISOString(),
actions: [],
executionStatus: {
status: 'ok',
lastExecutionDate: new Date().toISOString(),
},
},
score: 1,
references: [],
},
],
});
const alertsClient = new AlertsClient(alertsClientParams);
const result = await alertsClient.hasDecryptionFailures();
expect(result).toBe(true);
expect(unsecuredSavedObjectsClient.find).toHaveBeenCalledTimes(1);
});

test('return false if no alerts with a decryption error', async () => {
unsecuredSavedObjectsClient.find.mockResolvedValueOnce({
total: 2,
per_page: 10,
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: new Date().toISOString(),
error: {
reason: 'unknown',
message: 'Failed',
},
},
},
score: 1,
references: [
{
name: 'action_0',
type: 'action',
id: '1',
},
],
},
{
id: '2',
type: 'alert',
attributes: {
alertTypeId: 'myType',
schedule: { interval: '1s' },
params: {
bar: true,
},
createdAt: new Date().toISOString(),
actions: [],
executionStatus: {
status: 'ok',
lastExecutionDate: new Date().toISOString(),
},
},
score: 1,
references: [],
},
],
});
const alertsClient = new AlertsClient(alertsClientParams);
const result = await alertsClient.hasDecryptionFailures();
expect(result).toBe(false);
});
});

describe('delete()', () => {
let alertsClient: AlertsClient;
const existingAlert = {
Expand Down
11 changes: 11 additions & 0 deletions x-pack/plugins/alerts/server/alerts_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,17 @@ export class AlertsClient {
};
}

public async hasDecryptionFailures(): Promise<boolean> {
YulNaumenko marked this conversation as resolved.
Show resolved Hide resolved
const { saved_objects: data } = await this.unsecuredSavedObjectsClient.find<RawAlert>({
filter: 'alert.attributes.executionStatus.status:error',
fields: ['executionStatus'],
type: 'alert',
});
return (
data.filter((item) => item.attributes.executionStatus.error?.reason === 'decrypt').length > 0
YulNaumenko marked this conversation as resolved.
Show resolved Hide resolved
YulNaumenko marked this conversation as resolved.
Show resolved Hide resolved
);
}

public async delete({ id }: { id: string }) {
let taskIdToRemove: string | undefined | null;
let apiKeyToInvalidate: string | null = null;
Expand Down
74 changes: 74 additions & 0 deletions x-pack/plugins/alerts/server/health/get_state.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* 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 { taskManagerMock } from '../../../task_manager/server/task_manager.mock';
import { healthStatus$ } from '.';
import { TaskStatus } from '../../../task_manager/server';

describe('healthStatus$()', () => {
const mockTaskManager = taskManagerMock.start();

it('should return an object with the "unavailable" level and proper summary of "Alerting framework is unhealthy"', async () => {
mockTaskManager.get.mockReturnValue(
new Promise((_resolve, _reject) => {
return {
id: 'test',
attempts: 0,
status: TaskStatus.Running,
version: '123',
runAt: new Date(),
scheduledAt: new Date(),
startedAt: new Date(),
retryAt: new Date(Date.now() + 5 * 60 * 1000),
state: {
runs: 1,
isHealthy: false,
},
taskType: 'alerting:alerting_health_check',
params: {
alertId: '1',
},
ownerId: null,
};
})
);
healthStatus$(mockTaskManager).subscribe(
(val: { level: Readonly<unknown>; summary: string }) => {
expect(val.level).toBe(false);
}
);
});

it('should return an object with the "available" level and proper summary of "Alerting framework is healthy"', async () => {
mockTaskManager.get.mockReturnValue(
new Promise((_resolve, _reject) => {
return {
id: 'test',
attempts: 0,
status: TaskStatus.Running,
version: '123',
runAt: new Date(),
scheduledAt: new Date(),
startedAt: new Date(),
retryAt: new Date(Date.now() + 5 * 60 * 1000),
state: {
runs: 1,
isHealthy: true,
},
taskType: 'alerting:alerting_health_check',
params: {
alertId: '1',
},
ownerId: null,
};
})
);
healthStatus$(mockTaskManager).subscribe(
(val: { level: Readonly<unknown>; summary: string }) => {
expect(val.level).toBe(true);
}
);
});
});
51 changes: 51 additions & 0 deletions x-pack/plugins/alerts/server/health/get_state.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;
* you may not use this file except in compliance with the Elastic License.
*/

import { interval } from 'rxjs';
import { catchError, switchMap } from 'rxjs/operators';
import { get } from 'lodash';
import { ServiceStatusLevels } from '../../../../../src/core/server';
import { TaskManagerStartContract } from '../../../task_manager/server';
import { HEALTH_TASK_ID } from './task';

async function getLatestTaskState(taskManager: TaskManagerStartContract) {
try {
const result = await taskManager.get(HEALTH_TASK_ID);
return result;
} catch (err) {
const errMessage = err && err.message ? err.message : err.toString();
if (!errMessage.includes('NotInitialized')) {
throw err;
}
}

return null;
}

export const healthStatus$ = (taskManager: TaskManagerStartContract) => {
YulNaumenko marked this conversation as resolved.
Show resolved Hide resolved
return interval(1000).pipe(
YulNaumenko marked this conversation as resolved.
Show resolved Hide resolved
switchMap(async () => {
const doc = await getLatestTaskState(taskManager);
const body = get(doc, 'state');
YulNaumenko marked this conversation as resolved.
Show resolved Hide resolved
if (body?.isHealthy) {
return {
level: ServiceStatusLevels.available,
summary: 'Alerting framework is healthy',
YulNaumenko marked this conversation as resolved.
Show resolved Hide resolved
};
} else {
return {
level: ServiceStatusLevels.unavailable,
YulNaumenko marked this conversation as resolved.
Show resolved Hide resolved
summary: 'Alerting framework is unhealthy',
};
}
}),
catchError(async (error) => ({
level: ServiceStatusLevels.unavailable,
summary: `Alerting framework is unhealthy`,
meta: { error },
}))
);
};
8 changes: 8 additions & 0 deletions x-pack/plugins/alerts/server/health/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.
*/

export { healthStatus$ } from './get_state';
export { scheduleAlertingHealthCheck, initializeAlertingHealth } from './task';
Loading