Skip to content

Commit

Permalink
[ML] Fix license subscription race condition. (elastic#70074)
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
walterra committed Jun 30, 2020
1 parent 58bd509 commit 4dc19a7
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 5 deletions.
6 changes: 3 additions & 3 deletions x-pack/plugins/ml/public/application/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ export const renderApp = (

deps.kibanaLegacy.loadFontAwesome();

const mlLicense = setLicenseCache(deps.licensing);

appMountParams.onAppLeave((actions) => actions.default());

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

return () => {
mlLicense.unsubscribe();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import { LicensingPluginSetup } from '../../../../licensing/public';
import { MlLicense } from '../../../common/license';
import { MlClientLicense } from './ml_client_license';

let mlLicense: MlClientLicense | null = null;
Expand All @@ -16,9 +17,12 @@ let mlLicense: MlClientLicense | null = null;
* @param {LicensingPluginSetup} licensingSetup
* @returns {MlClientLicense}
*/
export function setLicenseCache(licensingSetup: LicensingPluginSetup) {
export function setLicenseCache(
licensingSetup: LicensingPluginSetup,
postInitFunctions?: Array<(lic: MlLicense) => void>
) {
mlLicense = new MlClientLicense();
mlLicense.setup(licensingSetup.license$);
mlLicense.setup(licensingSetup.license$, postInitFunctions);
return mlLicense;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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 { Observable, Subject } from 'rxjs';
import { ILicense } from '../../../../licensing/common/types';

import { MlClientLicense } from './ml_client_license';

describe('MlClientLicense', () => {
test('should miss the license update when initialized without postInitFunction', () => {
const mlLicense = new MlClientLicense();

// upon instantiation the full license doesn't get set
expect(mlLicense.isFullLicense()).toBe(false);

const license$ = new Subject();

mlLicense.setup(license$ as Observable<ILicense>);

// if the observable wasn't triggered the full license is still not set
expect(mlLicense.isFullLicense()).toBe(false);

license$.next({
check: () => ({ state: 'valid' }),
getFeature: () => ({ isEnabled: true }),
status: 'valid',
});

// once the observable triggered the license should be set
expect(mlLicense.isFullLicense()).toBe(true);
});

test('should not miss the license update when initialized with postInitFunction', (done) => {
const mlLicense = new MlClientLicense();

// upon instantiation the full license doesn't get set
expect(mlLicense.isFullLicense()).toBe(false);

const license$ = new Subject();

mlLicense.setup(license$ as Observable<ILicense>, [
(license) => {
// when passed in via postInitFunction callback, the license should be valid
// even if the license$ observable gets triggered after this setup.
expect(license.isFullLicense()).toBe(true);
done();
},
]);

license$.next({
check: () => ({ state: 'valid' }),
getFeature: () => ({ isEnabled: true }),
status: 'valid',
});
});
});

0 comments on commit 4dc19a7

Please sign in to comment.