mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [[licensing] Remove unnecessary refresh calls (#194499)](https://github.com/elastic/kibana/pull/194499) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Alejandro Fernández Haro","email":"alejandro.haro@elastic.co"},"sourceCommit":{"committedDate":"2024-10-03T15:42:39Z","message":"[licensing] Remove unnecessary refresh calls (#194499)","sha":"f3f53e054237087aab8590084cb7c8c10972427c","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Core","Team:Presentation","release_note:skip","v9.0.0","Team:Cloud Security","backport:prev-minor"],"title":"[licensing] Remove unnecessary refresh calls","number":194499,"url":"https://github.com/elastic/kibana/pull/194499","mergeCommit":{"message":"[licensing] Remove unnecessary refresh calls (#194499)","sha":"f3f53e054237087aab8590084cb7c8c10972427c"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/194499","number":194499,"mergeCommit":{"message":"[licensing] Remove unnecessary refresh calls (#194499)","sha":"f3f53e054237087aab8590084cb7c8c10972427c"}}]}] BACKPORT--> Co-authored-by: Alejandro Fernández Haro <alejandro.haro@elastic.co>
This commit is contained in:
parent
71b6f0b5e7
commit
026a8e43c8
16 changed files with 104 additions and 28 deletions
|
@ -32,7 +32,7 @@ export function getIsEnterprisePlus() {
|
|||
}
|
||||
|
||||
export async function setLicensingPluginStart(licensingPlugin: LicensingPluginStart) {
|
||||
const license = await licensingPlugin.refresh();
|
||||
const license = await licensingPlugin.getLicense();
|
||||
updateLicenseState(license);
|
||||
licensingPlugin.license$.subscribe(updateLicenseState);
|
||||
}
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { ILicense, LicensingPluginSetup } from '@kbn/licensing-plugin/server';
|
||||
import { Plugin, PluginInitializerContext } from '@kbn/core-plugins-server';
|
||||
import { CoreSetup } from '@kbn/core-lifecycle-server';
|
||||
import { MapConfig } from './config';
|
||||
import type { ILicense, LicensingPluginStart } from '@kbn/licensing-plugin/server';
|
||||
import type { Plugin, PluginInitializerContext } from '@kbn/core-plugins-server';
|
||||
import type { CoreSetup } from '@kbn/core-lifecycle-server';
|
||||
import type { MapConfig } from './config';
|
||||
import { LICENSE_CHECK_ID, EMSSettings } from '../common';
|
||||
|
||||
export interface MapsEmsPluginServerSetup {
|
||||
|
@ -18,8 +18,8 @@ export interface MapsEmsPluginServerSetup {
|
|||
createEMSSettings: () => EMSSettings;
|
||||
}
|
||||
|
||||
interface MapsEmsSetupServerDependencies {
|
||||
licensing?: LicensingPluginSetup;
|
||||
interface MapsEmsStartServerDependencies {
|
||||
licensing?: LicensingPluginStart;
|
||||
}
|
||||
|
||||
export class MapsEmsPlugin implements Plugin<MapsEmsPluginServerSetup> {
|
||||
|
@ -29,22 +29,20 @@ export class MapsEmsPlugin implements Plugin<MapsEmsPluginServerSetup> {
|
|||
this._initializerContext = initializerContext;
|
||||
}
|
||||
|
||||
public setup(core: CoreSetup, plugins: MapsEmsSetupServerDependencies) {
|
||||
public setup(core: CoreSetup<MapsEmsStartServerDependencies>) {
|
||||
const mapConfig = this._initializerContext.config.get();
|
||||
|
||||
let isEnterprisePlus = false;
|
||||
if (plugins.licensing) {
|
||||
function updateLicenseState(license: ILicense) {
|
||||
const enterprise = license.check(LICENSE_CHECK_ID, 'enterprise');
|
||||
isEnterprisePlus = enterprise.state === 'valid';
|
||||
}
|
||||
|
||||
plugins.licensing
|
||||
.refresh()
|
||||
.then(updateLicenseState)
|
||||
.catch(() => {});
|
||||
plugins.licensing.license$.subscribe(updateLicenseState);
|
||||
function updateLicenseState(license: ILicense) {
|
||||
const enterprise = license.check(LICENSE_CHECK_ID, 'enterprise');
|
||||
isEnterprisePlus = enterprise.state === 'valid';
|
||||
}
|
||||
core
|
||||
.getStartServices()
|
||||
.then(([_, { licensing }]) => {
|
||||
licensing?.license$.subscribe(updateLicenseState);
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
return {
|
||||
config: mapConfig,
|
||||
|
|
|
@ -16,7 +16,7 @@ export const useSubscriptionStatus = () => {
|
|||
const { licensing } = useKibana().services;
|
||||
const { isCloudEnabled } = useContext(SetupContext);
|
||||
return useQuery([SUBSCRIPTION_QUERY_KEY], async () => {
|
||||
const license = await licensing.refresh();
|
||||
const license = await licensing.getLicense();
|
||||
return isSubscriptionAllowed(isCloudEnabled, license);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -61,7 +61,7 @@ export class CloudDefendPlugin implements Plugin<CloudDefendPluginSetup, CloudDe
|
|||
plugins.fleet.registerExternalCallback(
|
||||
'packagePolicyCreate',
|
||||
async (packagePolicy: NewPackagePolicy): Promise<NewPackagePolicy> => {
|
||||
const license = await plugins.licensing.refresh();
|
||||
const license = await plugins.licensing.getLicense();
|
||||
if (isCloudDefendPackage(packagePolicy.package?.name)) {
|
||||
if (!isSubscriptionAllowed(this.isCloudEnabled, license)) {
|
||||
throw new Error(
|
||||
|
|
|
@ -17,7 +17,7 @@ export const useIsSubscriptionStatusValid = () => {
|
|||
const { isCloudEnabled } = useContext(SetupContext);
|
||||
|
||||
return useQuery([SUBSCRIPTION_QUERY_KEY], async () => {
|
||||
const license = await licensing.refresh();
|
||||
const license = await licensing.getLicense();
|
||||
return isSubscriptionAllowed(isCloudEnabled, license);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -115,7 +115,7 @@ export class CspPlugin
|
|||
plugins.fleet.registerExternalCallback(
|
||||
'packagePolicyCreate',
|
||||
async (packagePolicy: NewPackagePolicy): Promise<NewPackagePolicy> => {
|
||||
const license = await plugins.licensing.refresh();
|
||||
const license = await plugins.licensing.getLicense();
|
||||
if (isCspPackage(packagePolicy.package?.name)) {
|
||||
if (!isSubscriptionAllowed(this.isCloudEnabled, license)) {
|
||||
throw new Error(
|
||||
|
|
|
@ -8,13 +8,15 @@ Retrieves license data from Elasticsearch and becomes a source of license data f
|
|||
## API:
|
||||
### Server-side
|
||||
The licensing plugin retrieves license data from **Elasticsearch** at regular configurable intervals.
|
||||
- `license$: Observable<ILicense>` Provides a steam of license data [ILicense](./common/types.ts). Plugin emits new value whenever it detects changes in license info. If the plugin cannot retrieve a license from **Elasticsearch**, it will emit `an empty license` object.
|
||||
- `refresh: () => Promise<ILicense>` allows a plugin to enforce license retrieval.
|
||||
- `license$: Observable<ILicense>` Provides a steam of license data [ILicense](./common/types.ts). Plugin emits new value whenever it detects changes in license info. If the plugin cannot retrieve a license from **Elasticsearch**, it will emit `an empty license` object.
|
||||
- `getLicense(): Promise<ILicense>` returns the latest license data retrieved or waits for it to be resolved.
|
||||
- `refresh: () => Promise<ILicense>` triggers the licensing information re-fetch.
|
||||
|
||||
### Client-side
|
||||
The licensing plugin retrieves license data from **licensing Kibana plugin** and does not communicate with Elasticsearch directly.
|
||||
- `license$: Observable<ILicense>` Provides a steam of license data [ILicense](./common/types.ts). Plugin emits new value whenever it detects changes in license info. If the plugin cannot retrieve a license from **Kibana**, it will emit `an empty license` object.
|
||||
- `refresh: () => Promise<ILicense>` allows a plugin to enforce license retrieval.
|
||||
- `getLicense(): Promise<ILicense>` returns the latest license data retrieved or waits for it to be resolved.
|
||||
- `refresh: () => Promise<ILicense>` triggers the licensing information re-fetch.
|
||||
|
||||
## Migration example
|
||||
The new platform licensing plugin became stateless now. It means that instead of storing all your data from `checkLicense` within the plugin, you should react on license data change on both the client and server sides.
|
||||
|
|
|
@ -26,6 +26,7 @@ const createStartMock = () => {
|
|||
const license = licenseMock.createLicense();
|
||||
const mock: jest.Mocked<LicensingPluginStart> = {
|
||||
license$: new BehaviorSubject(license),
|
||||
getLicense: jest.fn(),
|
||||
refresh: jest.fn(),
|
||||
featureUsage: featureUsageMock.createStart(),
|
||||
};
|
||||
|
|
|
@ -75,6 +75,36 @@ describe('licensing plugin', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('#getLicense', () => {
|
||||
it('awaits for the license and returns it', async () => {
|
||||
const sessionStorage = coreMock.createStorage();
|
||||
plugin = new LicensingPlugin(coreMock.createPluginInitializerContext(), sessionStorage);
|
||||
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const firstLicense = licenseMock.createLicense({
|
||||
license: { uid: 'first', type: 'basic' },
|
||||
});
|
||||
coreSetup.http.get.mockResolvedValueOnce(firstLicense);
|
||||
|
||||
await plugin.setup(coreSetup);
|
||||
const { license$, getLicense, refresh } = await plugin.start(coreStart);
|
||||
const getLicensePromise = getLicense();
|
||||
|
||||
let fromObservable;
|
||||
license$.subscribe((license) => (fromObservable = license));
|
||||
await refresh(); // force the license fetch
|
||||
|
||||
const licenseResult = await getLicensePromise;
|
||||
expect(licenseResult.uid).toBe('first');
|
||||
expect(licenseResult).toBe(fromObservable);
|
||||
|
||||
const secondResult = await getLicense(); // retrieves the same license without refreshing
|
||||
expect(secondResult.uid).toBe('first');
|
||||
expect(secondResult).toBe(fromObservable);
|
||||
expect(coreSetup.http.get).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#license$', () => {
|
||||
it('starts with license saved in sessionStorage if available', async () => {
|
||||
const sessionStorage = coreMock.createStorage();
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Observable, Subject, Subscription } from 'rxjs';
|
||||
import { firstValueFrom, Observable, Subject, Subscription } from 'rxjs';
|
||||
|
||||
import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public';
|
||||
import { ILicense } from '../common/types';
|
||||
|
@ -134,6 +134,7 @@ export class LicensingPlugin implements Plugin<LicensingPluginSetup, LicensingPl
|
|||
}
|
||||
return {
|
||||
refresh: this.refresh,
|
||||
getLicense: async () => await firstValueFrom(this.license$!),
|
||||
license$: this.license$,
|
||||
featureUsage: this.featureUsage.start({ http: core.http }),
|
||||
};
|
||||
|
|
|
@ -36,6 +36,10 @@ export interface LicensingPluginStart {
|
|||
* Steam of licensing information {@link ILicense}.
|
||||
*/
|
||||
license$: Observable<ILicense>;
|
||||
/**
|
||||
* Retrieves the {@link ILicense | licensing information}
|
||||
*/
|
||||
getLicense(): Promise<ILicense>;
|
||||
/**
|
||||
* Triggers licensing information re-fetch.
|
||||
*/
|
||||
|
|
|
@ -30,6 +30,7 @@ const createStartMock = (): jest.Mocked<LicensingPluginStart> => {
|
|||
const license = licenseMock.createLicense();
|
||||
const mock = {
|
||||
license$: new BehaviorSubject(license),
|
||||
getLicense: jest.fn(),
|
||||
refresh: jest.fn(),
|
||||
createLicensePoller: jest.fn(),
|
||||
featureUsage: featureUsageMock.createStart(),
|
||||
|
|
|
@ -249,6 +249,38 @@ describe('licensing plugin', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('#getLicense', () => {
|
||||
it('awaits for the license and returns it', async () => {
|
||||
plugin = new LicensingPlugin(
|
||||
coreMock.createPluginInitializerContext({
|
||||
// disable polling mechanism
|
||||
api_polling_frequency: moment.duration(50000),
|
||||
license_cache_duration: moment.duration(1000),
|
||||
})
|
||||
);
|
||||
const esClient = createEsClient({
|
||||
license: buildRawLicense(),
|
||||
features: {},
|
||||
});
|
||||
|
||||
const coreSetup = createCoreSetupWith(esClient);
|
||||
plugin.setup(coreSetup);
|
||||
const { license$, getLicense } = plugin.start();
|
||||
|
||||
expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(0);
|
||||
|
||||
const firstLicense = await getLicense();
|
||||
let fromObservable;
|
||||
license$.subscribe((license) => (fromObservable = license));
|
||||
expect(firstLicense).toStrictEqual(fromObservable);
|
||||
expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(1); // the initial resolution
|
||||
|
||||
const secondLicense = await getLicense();
|
||||
expect(secondLicense).toStrictEqual(fromObservable);
|
||||
expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(1); // still only one call
|
||||
});
|
||||
});
|
||||
|
||||
describe('#refresh', () => {
|
||||
it('forces refresh immediately', async () => {
|
||||
plugin = new LicensingPlugin(
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
distinctUntilChanged,
|
||||
ReplaySubject,
|
||||
timer,
|
||||
firstValueFrom,
|
||||
} from 'rxjs';
|
||||
import moment from 'moment';
|
||||
import type { MaybePromise } from '@kbn/utility-types';
|
||||
|
@ -159,6 +160,7 @@ export class LicensingPlugin implements Plugin<LicensingPluginSetup, LicensingPl
|
|||
}
|
||||
return {
|
||||
refresh: this.refresh,
|
||||
getLicense: async () => await firstValueFrom(this.license$!),
|
||||
license$: this.license$,
|
||||
featureUsage: this.featureUsage.start(),
|
||||
createLicensePoller: this.createLicensePoller.bind(this),
|
||||
|
|
|
@ -75,6 +75,11 @@ export interface LicensingPluginStart {
|
|||
*/
|
||||
license$: Observable<ILicense>;
|
||||
|
||||
/**
|
||||
* Retrieves the {@link ILicense | licensing information}
|
||||
*/
|
||||
getLicense(): Promise<ILicense>;
|
||||
|
||||
/**
|
||||
* Triggers licensing information re-fetch.
|
||||
*/
|
||||
|
|
|
@ -50,7 +50,7 @@ export const whenLicenseInitialized = async (): Promise<void> => {
|
|||
};
|
||||
|
||||
export async function setLicensingPluginStart(licensingPlugin: LicensingPluginStart) {
|
||||
const license = await licensingPlugin.refresh();
|
||||
const license = await licensingPlugin.getLicense();
|
||||
updateLicenseState(license);
|
||||
|
||||
licensingPluginStart = licensingPlugin;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue