Skip to content

Commit efb2f8a

Browse files
committed
Merge branch 'master' of github.com:elastic/kibana into feature-rename-fleet-plugin
2 parents 4384e3f + 802c6dc commit efb2f8a

76 files changed

Lines changed: 2196 additions & 417 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/settings/apm-settings.asciidoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ Changing these settings may disable features of the APM App.
4343
| `xpack.apm.enabled`
4444
| Set to `false` to disable the APM app. Defaults to `true`.
4545

46+
| `xpack.apm.maxServiceEnvironments`
47+
| Maximum number of unique service environments recognized by the UI. Defaults to `100`.
48+
4649
| `xpack.apm.serviceMapFingerprintBucketSize`
4750
| Maximum number of unique transaction combinations sampled for generating service map focused on a specific service. Defaults to `100`.
4851

x-pack/plugins/alerts/common/alert.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@ export interface IntervalSchedule extends SavedObjectAttributes {
2020
export const AlertExecutionStatusValues = ['ok', 'active', 'error', 'pending', 'unknown'] as const;
2121
export type AlertExecutionStatuses = typeof AlertExecutionStatusValues[number];
2222

23-
export const AlertExecutionStatusErrorReasonValues = [
24-
'read',
25-
'decrypt',
26-
'execute',
27-
'unknown',
28-
] as const;
29-
export type AlertExecutionStatusErrorReasons = typeof AlertExecutionStatusErrorReasonValues[number];
23+
export enum AlertExecutionStatusErrorReasons {
24+
Read = 'read',
25+
Decrypt = 'decrypt',
26+
Execute = 'execute',
27+
Unknown = 'unknown',
28+
}
3029

3130
export interface AlertExecutionStatus {
3231
status: AlertExecutionStatuses;
@@ -74,3 +73,24 @@ export interface Alert {
7473
}
7574

7675
export type SanitizedAlert = Omit<Alert, 'apiKey'>;
76+
77+
export enum HealthStatus {
78+
OK = 'ok',
79+
Warning = 'warn',
80+
Error = 'error',
81+
}
82+
83+
export interface AlertsHealth {
84+
decryptionHealth: {
85+
status: HealthStatus;
86+
timestamp: string;
87+
};
88+
executionHealth: {
89+
status: HealthStatus;
90+
timestamp: string;
91+
};
92+
readHealth: {
93+
status: HealthStatus;
94+
timestamp: string;
95+
};
96+
}

x-pack/plugins/alerts/common/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7+
import { AlertsHealth } from './alert';
8+
79
export * from './alert';
810
export * from './alert_type';
911
export * from './alert_instance';
@@ -19,6 +21,7 @@ export interface ActionGroup {
1921
export interface AlertingFrameworkHealth {
2022
isSufficientlySecure: boolean;
2123
hasPermanentEncryptionKey: boolean;
24+
alertingFrameworkHeath: AlertsHealth;
2225
}
2326

2427
export const BASE_ALERT_API_PATH = '/api/alerts';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import { configSchema } from './config';
7+
8+
describe('config validation', () => {
9+
test('alerts defaults', () => {
10+
const config: Record<string, unknown> = {};
11+
expect(configSchema.validate(config)).toMatchInlineSnapshot(`
12+
Object {
13+
"healthCheck": Object {
14+
"interval": "60m",
15+
},
16+
}
17+
`);
18+
});
19+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { schema, TypeOf } from '@kbn/config-schema';
8+
import { validateDurationSchema } from './lib';
9+
10+
export const configSchema = schema.object({
11+
healthCheck: schema.object({
12+
interval: schema.string({ validate: validateDurationSchema, defaultValue: '60m' }),
13+
}),
14+
});
15+
16+
export type AlertsConfig = TypeOf<typeof configSchema>;
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import { savedObjectsRepositoryMock } from '../../../../../src/core/server/mocks';
7+
import { AlertExecutionStatusErrorReasons, HealthStatus } from '../types';
8+
import { getHealth } from './get_health';
9+
10+
const savedObjectsRepository = savedObjectsRepositoryMock.create();
11+
12+
describe('getHealth()', () => {
13+
test('return true if some of alerts has a decryption error', async () => {
14+
const lastExecutionDateError = new Date().toISOString();
15+
const lastExecutionDate = new Date().toISOString();
16+
savedObjectsRepository.find.mockResolvedValueOnce({
17+
total: 1,
18+
per_page: 1,
19+
page: 1,
20+
saved_objects: [
21+
{
22+
id: '1',
23+
type: 'alert',
24+
attributes: {
25+
alertTypeId: 'myType',
26+
schedule: { interval: '10s' },
27+
params: {
28+
bar: true,
29+
},
30+
createdAt: new Date().toISOString(),
31+
actions: [
32+
{
33+
group: 'default',
34+
actionRef: 'action_0',
35+
params: {
36+
foo: true,
37+
},
38+
},
39+
],
40+
executionStatus: {
41+
status: 'error',
42+
lastExecutionDate: lastExecutionDateError,
43+
error: {
44+
reason: AlertExecutionStatusErrorReasons.Decrypt,
45+
message: 'Failed decrypt',
46+
},
47+
},
48+
},
49+
score: 1,
50+
references: [
51+
{
52+
name: 'action_0',
53+
type: 'action',
54+
id: '1',
55+
},
56+
],
57+
},
58+
],
59+
});
60+
savedObjectsRepository.find.mockResolvedValueOnce({
61+
total: 0,
62+
per_page: 10,
63+
page: 1,
64+
saved_objects: [],
65+
});
66+
67+
savedObjectsRepository.find.mockResolvedValueOnce({
68+
total: 0,
69+
per_page: 10,
70+
page: 1,
71+
saved_objects: [],
72+
});
73+
74+
savedObjectsRepository.find.mockResolvedValueOnce({
75+
total: 1,
76+
per_page: 1,
77+
page: 1,
78+
saved_objects: [
79+
{
80+
id: '2',
81+
type: 'alert',
82+
attributes: {
83+
alertTypeId: 'myType',
84+
schedule: { interval: '1s' },
85+
params: {
86+
bar: true,
87+
},
88+
createdAt: new Date().toISOString(),
89+
actions: [],
90+
executionStatus: {
91+
status: 'ok',
92+
lastExecutionDate,
93+
},
94+
},
95+
score: 1,
96+
references: [],
97+
},
98+
],
99+
});
100+
const result = await getHealth(savedObjectsRepository);
101+
expect(result).toStrictEqual({
102+
executionHealth: {
103+
status: HealthStatus.OK,
104+
timestamp: lastExecutionDate,
105+
},
106+
readHealth: {
107+
status: HealthStatus.OK,
108+
timestamp: lastExecutionDate,
109+
},
110+
decryptionHealth: {
111+
status: HealthStatus.Warning,
112+
timestamp: lastExecutionDateError,
113+
},
114+
});
115+
expect(savedObjectsRepository.find).toHaveBeenCalledTimes(4);
116+
});
117+
118+
test('return false if no alerts with a decryption error', async () => {
119+
const lastExecutionDateError = new Date().toISOString();
120+
const lastExecutionDate = new Date().toISOString();
121+
savedObjectsRepository.find.mockResolvedValueOnce({
122+
total: 0,
123+
per_page: 10,
124+
page: 1,
125+
saved_objects: [],
126+
});
127+
128+
savedObjectsRepository.find.mockResolvedValueOnce({
129+
total: 1,
130+
per_page: 1,
131+
page: 1,
132+
saved_objects: [
133+
{
134+
id: '1',
135+
type: 'alert',
136+
attributes: {
137+
alertTypeId: 'myType',
138+
schedule: { interval: '10s' },
139+
params: {
140+
bar: true,
141+
},
142+
createdAt: new Date().toISOString(),
143+
actions: [
144+
{
145+
group: 'default',
146+
actionRef: 'action_0',
147+
params: {
148+
foo: true,
149+
},
150+
},
151+
],
152+
executionStatus: {
153+
status: 'error',
154+
lastExecutionDate: lastExecutionDateError,
155+
error: {
156+
reason: AlertExecutionStatusErrorReasons.Execute,
157+
message: 'Failed',
158+
},
159+
},
160+
},
161+
score: 1,
162+
references: [
163+
{
164+
name: 'action_0',
165+
type: 'action',
166+
id: '1',
167+
},
168+
],
169+
},
170+
],
171+
});
172+
savedObjectsRepository.find.mockResolvedValueOnce({
173+
total: 0,
174+
per_page: 10,
175+
page: 1,
176+
saved_objects: [],
177+
});
178+
179+
savedObjectsRepository.find.mockResolvedValueOnce({
180+
total: 1,
181+
per_page: 1,
182+
page: 1,
183+
saved_objects: [
184+
{
185+
id: '2',
186+
type: 'alert',
187+
attributes: {
188+
alertTypeId: 'myType',
189+
schedule: { interval: '1s' },
190+
params: {
191+
bar: true,
192+
},
193+
createdAt: new Date().toISOString(),
194+
actions: [],
195+
executionStatus: {
196+
status: 'ok',
197+
lastExecutionDate,
198+
},
199+
},
200+
score: 1,
201+
references: [],
202+
},
203+
],
204+
});
205+
const result = await getHealth(savedObjectsRepository);
206+
expect(result).toStrictEqual({
207+
executionHealth: {
208+
status: HealthStatus.Warning,
209+
timestamp: lastExecutionDateError,
210+
},
211+
readHealth: {
212+
status: HealthStatus.OK,
213+
timestamp: lastExecutionDate,
214+
},
215+
decryptionHealth: {
216+
status: HealthStatus.OK,
217+
timestamp: lastExecutionDate,
218+
},
219+
});
220+
});
221+
});

0 commit comments

Comments
 (0)