Skip to content

Commit 4dc19a7

Browse files
committed
[ML] Fix license subscription race condition. (#70074)
Fixes a race condition where the ML plugin would be mounted before receiving its first license information update and thus redirecting to a fallback page (Kibana Home, Space-Chooser or Data Visualizer page depending on the setup).
1 parent 58bd509 commit 4dc19a7

3 files changed

Lines changed: 68 additions & 5 deletions

File tree

x-pack/plugins/ml/public/application/app.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ export const renderApp = (
8080

8181
deps.kibanaLegacy.loadFontAwesome();
8282

83-
const mlLicense = setLicenseCache(deps.licensing);
84-
8583
appMountParams.onAppLeave((actions) => actions.default());
8684

87-
ReactDOM.render(<App coreStart={coreStart} deps={deps} />, appMountParams.element);
85+
const mlLicense = setLicenseCache(deps.licensing, [
86+
() => ReactDOM.render(<App coreStart={coreStart} deps={deps} />, appMountParams.element),
87+
]);
8888

8989
return () => {
9090
mlLicense.unsubscribe();

x-pack/plugins/ml/public/application/license/check_license.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
import { LicensingPluginSetup } from '../../../../licensing/public';
8+
import { MlLicense } from '../../../common/license';
89
import { MlClientLicense } from './ml_client_license';
910

1011
let mlLicense: MlClientLicense | null = null;
@@ -16,9 +17,12 @@ let mlLicense: MlClientLicense | null = null;
1617
* @param {LicensingPluginSetup} licensingSetup
1718
* @returns {MlClientLicense}
1819
*/
19-
export function setLicenseCache(licensingSetup: LicensingPluginSetup) {
20+
export function setLicenseCache(
21+
licensingSetup: LicensingPluginSetup,
22+
postInitFunctions?: Array<(lic: MlLicense) => void>
23+
) {
2024
mlLicense = new MlClientLicense();
21-
mlLicense.setup(licensingSetup.license$);
25+
mlLicense.setup(licensingSetup.license$, postInitFunctions);
2226
return mlLicense;
2327
}
2428

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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 { Observable, Subject } from 'rxjs';
8+
import { ILicense } from '../../../../licensing/common/types';
9+
10+
import { MlClientLicense } from './ml_client_license';
11+
12+
describe('MlClientLicense', () => {
13+
test('should miss the license update when initialized without postInitFunction', () => {
14+
const mlLicense = new MlClientLicense();
15+
16+
// upon instantiation the full license doesn't get set
17+
expect(mlLicense.isFullLicense()).toBe(false);
18+
19+
const license$ = new Subject();
20+
21+
mlLicense.setup(license$ as Observable<ILicense>);
22+
23+
// if the observable wasn't triggered the full license is still not set
24+
expect(mlLicense.isFullLicense()).toBe(false);
25+
26+
license$.next({
27+
check: () => ({ state: 'valid' }),
28+
getFeature: () => ({ isEnabled: true }),
29+
status: 'valid',
30+
});
31+
32+
// once the observable triggered the license should be set
33+
expect(mlLicense.isFullLicense()).toBe(true);
34+
});
35+
36+
test('should not miss the license update when initialized with postInitFunction', (done) => {
37+
const mlLicense = new MlClientLicense();
38+
39+
// upon instantiation the full license doesn't get set
40+
expect(mlLicense.isFullLicense()).toBe(false);
41+
42+
const license$ = new Subject();
43+
44+
mlLicense.setup(license$ as Observable<ILicense>, [
45+
(license) => {
46+
// when passed in via postInitFunction callback, the license should be valid
47+
// even if the license$ observable gets triggered after this setup.
48+
expect(license.isFullLicense()).toBe(true);
49+
done();
50+
},
51+
]);
52+
53+
license$.next({
54+
check: () => ({ state: 'valid' }),
55+
getFeature: () => ({ isEnabled: true }),
56+
status: 'valid',
57+
});
58+
});
59+
});

0 commit comments

Comments
 (0)