Skip to content

Commit 3fb639f

Browse files
authored
[Monitoring] Fix bug where kibana crashes with collection disabled (#75335)
* Fix bug where kibana crashes with collection disabled * Do not set this to an empty object by default * Always create the bulk uploader, and fix this misplaced license check * Fix type issue * Add a couple basic tests * Fix type issue
1 parent 020a76b commit 3fb639f

File tree

3 files changed

+169
-26
lines changed

3 files changed

+169
-26
lines changed

x-pack/plugins/monitoring/server/license_service.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,13 @@ export class LicenseService {
3333
let rawLicense: Readonly<ILicense> | undefined;
3434
let licenseSubscription: Subscription | undefined = license$.subscribe((nextRawLicense) => {
3535
rawLicense = nextRawLicense;
36+
if (!rawLicense?.isAvailable) {
37+
log.warn(
38+
`X-Pack Monitoring Cluster Alerts will not be available: ${rawLicense?.getUnavailableReason()}`
39+
);
40+
}
3641
});
3742

38-
if (!rawLicense?.isAvailable) {
39-
log.warn(
40-
`X-Pack Monitoring Cluster Alerts will not be available: ${rawLicense?.getUnavailableReason()}`
41-
);
42-
}
43-
4443
return {
4544
refresh,
4645
license$,
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
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 { Plugin } from './plugin';
7+
import { combineLatest } from 'rxjs';
8+
// @ts-ignore
9+
import { initBulkUploader } from './kibana_monitoring';
10+
import { AlertsFactory } from './alerts';
11+
12+
jest.mock('rxjs', () => ({
13+
// @ts-ignore
14+
...jest.requireActual('rxjs'),
15+
combineLatest: jest.fn(),
16+
}));
17+
18+
jest.mock('./es_client/instantiate_client', () => ({
19+
instantiateClient: jest.fn(),
20+
}));
21+
22+
jest.mock('./license_service', () => ({
23+
LicenseService: jest.fn().mockImplementation(() => ({
24+
setup: jest.fn().mockImplementation(() => ({
25+
refresh: jest.fn(),
26+
})),
27+
})),
28+
}));
29+
30+
jest.mock('./kibana_monitoring', () => ({
31+
initBulkUploader: jest.fn(),
32+
}));
33+
34+
describe('Monitoring plugin', () => {
35+
const initializerContext = {
36+
logger: {
37+
get: jest.fn().mockImplementation(() => ({
38+
info: jest.fn(),
39+
})),
40+
},
41+
config: {
42+
create: jest.fn().mockImplementation(() => ({
43+
pipe: jest.fn().mockImplementation(() => ({
44+
toPromise: jest.fn(),
45+
})),
46+
})),
47+
legacy: {
48+
globalConfig$: {},
49+
},
50+
},
51+
env: {
52+
packageInfo: {
53+
version: '1.0.0',
54+
},
55+
},
56+
};
57+
58+
const coreSetup = {
59+
http: {
60+
createRouter: jest.fn(),
61+
getServerInfo: jest.fn().mockImplementation(() => ({
62+
port: 5601,
63+
})),
64+
basePath: {
65+
serverBasePath: '',
66+
},
67+
},
68+
uuid: {
69+
getInstanceUuid: jest.fn(),
70+
},
71+
elasticsearch: {
72+
legacy: {
73+
client: {},
74+
createClient: jest.fn(),
75+
},
76+
},
77+
};
78+
79+
const setupPlugins = {
80+
usageCollection: {
81+
getCollectorByType: jest.fn(),
82+
makeStatsCollector: jest.fn(),
83+
registerCollector: jest.fn(),
84+
},
85+
alerts: {
86+
registerType: jest.fn(),
87+
},
88+
};
89+
90+
let config = {};
91+
const defaultConfig = {
92+
ui: {
93+
elasticsearch: {},
94+
},
95+
kibana: {
96+
collection: {
97+
interval: 30000,
98+
},
99+
},
100+
};
101+
102+
beforeEach(() => {
103+
config = defaultConfig;
104+
(combineLatest as jest.Mock).mockImplementation(() => {
105+
return {
106+
pipe: jest.fn().mockImplementation(() => {
107+
return {
108+
toPromise: jest.fn().mockImplementation(() => {
109+
return [config, 2];
110+
}),
111+
};
112+
}),
113+
};
114+
});
115+
});
116+
117+
afterEach(() => {
118+
(setupPlugins.alerts.registerType as jest.Mock).mockReset();
119+
});
120+
121+
it('always create the bulk uploader', async () => {
122+
const setKibanaStatusGetter = jest.fn();
123+
(initBulkUploader as jest.Mock).mockImplementation(() => {
124+
return {
125+
setKibanaStatusGetter,
126+
};
127+
});
128+
const plugin = new Plugin(initializerContext as any);
129+
const contract = await plugin.setup(coreSetup as any, setupPlugins as any);
130+
contract.registerLegacyAPI(null as any);
131+
expect(setKibanaStatusGetter).toHaveBeenCalled();
132+
});
133+
134+
it('should register all alerts', async () => {
135+
const alerts = AlertsFactory.getAll();
136+
const plugin = new Plugin(initializerContext as any);
137+
await plugin.setup(coreSetup as any, setupPlugins as any);
138+
expect(setupPlugins.alerts.registerType).toHaveBeenCalledTimes(alerts.length);
139+
});
140+
});

x-pack/plugins/monitoring/server/plugin.ts

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export class Plugin {
7171
private licenseService = {} as MonitoringLicenseService;
7272
private monitoringCore = {} as MonitoringCore;
7373
private legacyShimDependencies = {} as LegacyShimDependencies;
74-
private bulkUploader = {} as IBulkUploader;
74+
private bulkUploader: IBulkUploader = {} as IBulkUploader;
7575

7676
constructor(initializerContext: PluginInitializerContext) {
7777
this.initializerContext = initializerContext;
@@ -152,28 +152,28 @@ export class Plugin {
152152
registerCollectors(plugins.usageCollection, config);
153153
}
154154

155-
// If collection is enabled, create the bulk uploader
155+
// Always create the bulk uploader
156156
const kibanaMonitoringLog = this.getLogger(KIBANA_MONITORING_LOGGING_TAG);
157+
const bulkUploader = (this.bulkUploader = initBulkUploader({
158+
elasticsearch: core.elasticsearch,
159+
config,
160+
log: kibanaMonitoringLog,
161+
kibanaStats: {
162+
uuid: core.uuid.getInstanceUuid(),
163+
name: serverInfo.name,
164+
index: get(legacyConfig, 'kibana.index'),
165+
host: serverInfo.hostname,
166+
locale: i18n.getLocale(),
167+
port: serverInfo.port.toString(),
168+
transport_address: `${serverInfo.hostname}:${serverInfo.port}`,
169+
version: this.initializerContext.env.packageInfo.version,
170+
snapshot: snapshotRegex.test(this.initializerContext.env.packageInfo.version),
171+
},
172+
}));
173+
174+
// If collection is enabled, start it
157175
const kibanaCollectionEnabled = config.kibana.collection.enabled;
158176
if (kibanaCollectionEnabled) {
159-
// Start kibana internal collection
160-
const bulkUploader = (this.bulkUploader = initBulkUploader({
161-
elasticsearch: core.elasticsearch,
162-
config,
163-
log: kibanaMonitoringLog,
164-
kibanaStats: {
165-
uuid: core.uuid.getInstanceUuid(),
166-
name: serverInfo.name,
167-
index: get(legacyConfig, 'kibana.index'),
168-
host: serverInfo.hostname,
169-
locale: i18n.getLocale(),
170-
port: serverInfo.port.toString(),
171-
transport_address: `${serverInfo.hostname}:${serverInfo.port}`,
172-
version: this.initializerContext.env.packageInfo.version,
173-
snapshot: snapshotRegex.test(this.initializerContext.env.packageInfo.version),
174-
},
175-
}));
176-
177177
// Do not use `this.licenseService` as that looks at the monitoring cluster
178178
// whereas we want to check the production cluster here
179179
if (plugins.licensing) {
@@ -188,6 +188,10 @@ export class Plugin {
188188
bulkUploader.handleNotEnabled();
189189
}
190190
});
191+
} else {
192+
kibanaMonitoringLog.warn(
193+
'Internal collection for Kibana monitoring is disabled due to missing license information.'
194+
);
191195
}
192196
} else {
193197
kibanaMonitoringLog.info(

0 commit comments

Comments
 (0)