From 70618f66ac5a08af42768ee3a5e7746c5b3be695 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Mon, 22 Feb 2021 12:13:26 +0100 Subject: [PATCH 01/11] use new client for licensing API --- x-pack/plugins/licensing/server/plugin.ts | 48 +++++++---------------- x-pack/plugins/licensing/server/types.ts | 11 ++++-- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/licensing/server/plugin.ts b/x-pack/plugins/licensing/server/plugin.ts index a1ec5e9450b28b..082b3508a3ce3f 100644 --- a/x-pack/plugins/licensing/server/plugin.ts +++ b/x-pack/plugins/licensing/server/plugin.ts @@ -10,14 +10,14 @@ import moment from 'moment'; import { createHash } from 'crypto'; import stringify from 'json-stable-stringify'; +import { MaybePromise } from '@kbn/utility-types'; +import { isPromise } from '@kbn/std'; import { CoreSetup, Logger, Plugin, PluginInitializerContext, - ILegacyClusterClient, - ILegacyScopedClusterClient, - ScopeableRequest, + IClusterClient, } from 'src/core/server'; import { ILicense, PublicLicense, PublicFeatures } from '../common/types'; @@ -98,32 +98,12 @@ export class LicensingPlugin implements Plugin - ): ReturnType { - const [coreStart] = await core.getStartServices(); - const client = coreStart.elasticsearch.legacy.client; - return await client.callAsInternalUser(...args); - } - - const client: ILegacyClusterClient = { - callAsInternalUser, - asScoped(request?: ScopeableRequest): ILegacyScopedClusterClient { - return { - async callAsCurrentUser( - ...args: Parameters - ): ReturnType { - const [coreStart] = await core.getStartServices(); - const _client = coreStart.elasticsearch.legacy.client; - return await _client.asScoped(request).callAsCurrentUser(...args); - }, - callAsInternalUser, - }; - }, - }; + const clientPromise = core.getStartServices().then(([{ elasticsearch }]) => { + return elasticsearch.client; + }); const { refresh, license$ } = this.createLicensePoller( - client, + clientPromise, pollingFrequency.asMilliseconds() ); @@ -148,7 +128,10 @@ export class LicensingPlugin implements Plugin, + pollingFrequency: number + ) { this.logger.debug(`Polling Elasticsearch License API with frequency ${pollingFrequency}ms.`); const intervalRefresh$ = timer(0, pollingFrequency); @@ -177,13 +160,12 @@ export class LicensingPlugin implements Plugin => { + private fetchLicense = async (clusterClient: MaybePromise): Promise => { + const client = isPromise(clusterClient) ? await clusterClient : clusterClient; try { - const response = await clusterClient.callAsInternalUser('transport.request', { - method: 'GET', - path: '/_xpack?accept_enterprise=true', + const { body: response } = await client.asInternalUser.xpack.info({ + accept_enterprise: true, }); - const normalizedLicense = response.license ? normalizeServerLicense(response.license) : undefined; diff --git a/x-pack/plugins/licensing/server/types.ts b/x-pack/plugins/licensing/server/types.ts index 8b2d9599e0ffe2..b2155cab82924f 100644 --- a/x-pack/plugins/licensing/server/types.ts +++ b/x-pack/plugins/licensing/server/types.ts @@ -6,13 +6,14 @@ */ import { Observable } from 'rxjs'; -import type { ILegacyClusterClient, IRouter, RequestHandlerContext } from 'src/core/server'; +import type { IClusterClient, IRouter, RequestHandlerContext } from 'src/core/server'; import { ILicense, LicenseStatus, LicenseType } from '../common/types'; import { FeatureUsageServiceSetup, FeatureUsageServiceStart } from './services'; export interface ElasticsearchError extends Error { status?: number; } + /** * Result from remote request fetching raw feature set. * @internal @@ -70,11 +71,13 @@ export interface LicensingPluginSetup { * @deprecated in favour of the counterpart provided from start contract */ license$: Observable; + /** * Triggers licensing information re-fetch. * @deprecated in favour of the counterpart provided from start contract */ refresh(): Promise; + /** * Creates a license poller to retrieve a license data with. * Allows a plugin to configure a cluster to retrieve data from at @@ -82,7 +85,7 @@ export interface LicensingPluginSetup { * @deprecated in favour of the counterpart provided from start contract */ createLicensePoller: ( - clusterClient: ILegacyClusterClient, + clusterClient: IClusterClient, pollingFrequency: number ) => { license$: Observable; refresh(): Promise }; /** @@ -97,17 +100,19 @@ export interface LicensingPluginStart { * Steam of licensing information {@link ILicense}. */ license$: Observable; + /** * Triggers licensing information re-fetch. */ refresh(): Promise; + /** * Creates a license poller to retrieve a license data with. * Allows a plugin to configure a cluster to retrieve data from at * given polling frequency. */ createLicensePoller: ( - clusterClient: ILegacyClusterClient, + clusterClient: IClusterClient, pollingFrequency: number ) => { license$: Observable; refresh(): Promise }; /** From 4a61b646e6c19b684ce7f07c024bc8005940c29c Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Mon, 22 Feb 2021 12:39:29 +0100 Subject: [PATCH 02/11] add logs --- src/core/server/status/status_service.ts | 28 +++++++++++++++-------- x-pack/plugins/licensing/server/plugin.ts | 24 ++++++++++++++++++- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/core/server/status/status_service.ts b/src/core/server/status/status_service.ts index 09cf5b92b2b8a3..95fb760d2a2405 100644 --- a/src/core/server/status/status_service.ts +++ b/src/core/server/status/status_service.ts @@ -7,7 +7,15 @@ */ import { Observable, combineLatest, Subscription } from 'rxjs'; -import { map, distinctUntilChanged, shareReplay, take, debounceTime } from 'rxjs/operators'; +import { + map, + distinctUntilChanged, + shareReplay, + take, + debounceTime, + pairwise, + startWith, +} from 'rxjs/operators'; import { isDeepStrictEqual } from 'util'; import { CoreService } from '../../types'; @@ -39,6 +47,7 @@ export class StatusService implements CoreService { private readonly logger: Logger; private readonly config$: Observable; + private overall$?: Observable; private pluginsStatus?: PluginsStatusService; private overallSubscription?: Subscription; @@ -59,10 +68,7 @@ export class StatusService implements CoreService { const core$ = this.setupCoreStatus({ elasticsearch, savedObjects }); this.pluginsStatus = new PluginsStatusService({ core$, pluginDependencies }); - const overall$: Observable = combineLatest([ - core$, - this.pluginsStatus.getAll$(), - ]).pipe( + this.overall$ = combineLatest([core$, this.pluginsStatus.getAll$()]).pipe( // Prevent many emissions at once from dependency status resolution from making this too noisy debounceTime(500), map(([coreStatus, pluginsStatus]) => { @@ -78,7 +84,7 @@ export class StatusService implements CoreService { ); // Create an unused subscription to ensure all underlying lazy observables are started. - this.overallSubscription = overall$.subscribe(); + this.overallSubscription = this.overall$.subscribe(); const router = http.createRouter(''); registerStatusRoute({ @@ -91,7 +97,7 @@ export class StatusService implements CoreService { }, metrics, status: { - overall$, + overall$: this.overall$, plugins$: this.pluginsStatus.getAll$(), core$, }, @@ -99,7 +105,7 @@ export class StatusService implements CoreService { return { core$, - overall$, + overall$: this.overall$, plugins: { set: this.pluginsStatus.set.bind(this.pluginsStatus), getDependenciesStatus$: this.pluginsStatus.getDependenciesStatus$.bind(this.pluginsStatus), @@ -109,7 +115,11 @@ export class StatusService implements CoreService { }; } - public start() {} + public start() { + this.overall$!.pipe(startWith(undefined), pairwise()).subscribe(([oldStatus, newStatus]) => { + this.logger.info(`Kibana overall status is now ${newStatus!.level.toString()}`); + }); + } public stop() { if (this.overallSubscription) { diff --git a/x-pack/plugins/licensing/server/plugin.ts b/x-pack/plugins/licensing/server/plugin.ts index 082b3508a3ce3f..3e8976a27c3327 100644 --- a/x-pack/plugins/licensing/server/plugin.ts +++ b/x-pack/plugins/licensing/server/plugin.ts @@ -6,6 +6,7 @@ */ import { Observable, Subject, Subscription, timer } from 'rxjs'; +import { take, startWith, map } from 'rxjs/operators'; import moment from 'moment'; import { createHash } from 'crypto'; import stringify from 'json-stable-stringify'; @@ -18,7 +19,8 @@ import { Plugin, PluginInitializerContext, IClusterClient, -} from 'src/core/server'; + ServiceStatusLevels, +} from '../../../../src/core/server'; import { ILicense, PublicLicense, PublicFeatures } from '../common/types'; import { LicensingPluginSetup, LicensingPluginStart } from './types'; @@ -107,6 +109,26 @@ export class LicensingPlugin implements Plugin { + if (license) { + return { + level: ServiceStatusLevels.available, + summary: 'License fetched', + }; + } else { + return { + level: ServiceStatusLevels.degraded, + summary: 'license not fetched yet', + }; + } + }) + ) + ); + core.http.registerRouteHandlerContext( 'licensing', createRouteHandlerContext(license$, core.getStartServices) From bbcac5eeccdc304043b1ff2bc8382f6223ada3f4 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Mon, 22 Feb 2021 13:26:34 +0100 Subject: [PATCH 03/11] adapt unit tests --- .../plugins/licensing/server/plugin.test.ts | 123 ++++++++++-------- 1 file changed, 66 insertions(+), 57 deletions(-) diff --git a/x-pack/plugins/licensing/server/plugin.test.ts b/x-pack/plugins/licensing/server/plugin.test.ts index 446cd2baa1cc0a..ff358353647504 100644 --- a/x-pack/plugins/licensing/server/plugin.test.ts +++ b/x-pack/plugins/licensing/server/plugin.test.ts @@ -15,22 +15,22 @@ import { elasticsearchServiceMock, loggingSystemMock, } from '../../../../src/core/server/mocks'; -import { ILegacyClusterClient } from '../../../../src/core/server/'; +import { IClusterClient } from '../../../../src/core/server'; function buildRawLicense(options: Partial = {}): RawLicense { - const defaultRawLicense: RawLicense = { + return { uid: 'uid-000000001234', status: 'active', type: 'basic', mode: 'basic', expiry_date_in_millis: 1000, + ...options, }; - return Object.assign(defaultRawLicense, options); } const flushPromises = (ms = 50) => new Promise((res) => setTimeout(res, ms)); -function createCoreSetupWith(esClient: ILegacyClusterClient) { +function createCoreSetupWith(esClient: IClusterClient) { const coreSetup = coreMock.createSetup(); const coreStart = coreMock.createStart(); coreSetup.getStartServices.mockResolvedValue([ @@ -38,11 +38,7 @@ function createCoreSetupWith(esClient: ILegacyClusterClient) { ...coreStart, elasticsearch: { ...coreStart.elasticsearch, - legacy: { - ...coreStart.elasticsearch.legacy, - client: esClient, - createClient: jest.fn(), - }, + client: esClient, }, }, {}, @@ -52,6 +48,16 @@ function createCoreSetupWith(esClient: ILegacyClusterClient) { } describe('licensing plugin', () => { + const createEsClient = (response?: Record) => { + const client = elasticsearchServiceMock.createClusterClient(); + if (response) { + client.asInternalUser.xpack.info.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise(response) + ); + } + return client; + }; + describe('#start', () => { describe('#license$', () => { let plugin: LicensingPlugin; @@ -69,8 +75,7 @@ describe('licensing plugin', () => { }); it('returns license', async () => { - const esClient = elasticsearchServiceMock.createLegacyClusterClient(); - esClient.callAsInternalUser.mockResolvedValue({ + const esClient = createEsClient({ license: buildRawLicense(), features: {}, }); @@ -83,8 +88,7 @@ describe('licensing plugin', () => { }); it('calls `callAsInternalUser` with the correct parameters', async () => { - const esClient = elasticsearchServiceMock.createLegacyClusterClient(); - esClient.callAsInternalUser.mockResolvedValue({ + const esClient = createEsClient({ license: buildRawLicense(), features: {}, }); @@ -94,23 +98,22 @@ describe('licensing plugin', () => { const { license$ } = await plugin.start(); await license$.pipe(take(1)).toPromise(); - expect(esClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(esClient.callAsInternalUser).toHaveBeenCalledWith('transport.request', { - method: 'GET', - path: '/_xpack?accept_enterprise=true', + expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(1); + expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledWith({ + accept_enterprise: true, }); }); it('observable receives updated licenses', async () => { const types: LicenseType[] = ['basic', 'gold', 'platinum']; - const esClient = elasticsearchServiceMock.createLegacyClusterClient(); - esClient.callAsInternalUser.mockImplementation(() => - Promise.resolve({ + const esClient = createEsClient(); + esClient.asInternalUser.xpack.info.mockImplementation(() => { + return elasticsearchServiceMock.createSuccessTransportRequestPromise({ license: buildRawLicense({ type: types.shift() }), features: {}, - }) - ); + }); + }); const coreSetup = createCoreSetupWith(esClient); await plugin.setup(coreSetup); @@ -123,8 +126,8 @@ describe('licensing plugin', () => { }); it('returns a license with error when request fails', async () => { - const esClient = elasticsearchServiceMock.createLegacyClusterClient(); - esClient.callAsInternalUser.mockRejectedValue(new Error('test')); + const esClient = createEsClient(); + esClient.asInternalUser.xpack.info.mockRejectedValue(new Error('test')); const coreSetup = createCoreSetupWith(esClient); await plugin.setup(coreSetup); @@ -136,10 +139,10 @@ describe('licensing plugin', () => { }); it('generate error message when x-pack plugin was not installed', async () => { - const esClient = elasticsearchServiceMock.createLegacyClusterClient(); + const esClient = createEsClient(); const error: ElasticsearchError = new Error('reason'); error.status = 400; - esClient.callAsInternalUser.mockRejectedValue(error); + esClient.asInternalUser.xpack.info.mockRejectedValue(error); const coreSetup = createCoreSetupWith(esClient); await plugin.setup(coreSetup); @@ -154,26 +157,37 @@ describe('licensing plugin', () => { const error1 = new Error('reason-1'); const error2 = new Error('reason-2'); - const esClient = elasticsearchServiceMock.createLegacyClusterClient(); - - esClient.callAsInternalUser - .mockRejectedValueOnce(error1) - .mockRejectedValueOnce(error2) - .mockResolvedValue({ license: buildRawLicense(), features: {} }); + const esClient = createEsClient(); + let i = 0; + esClient.asInternalUser.xpack.info.mockImplementation(() => { + i++; + if (i === 1) { + return elasticsearchServiceMock.createErrorTransportRequestPromise(error1); + } + if (i === 2) { + return elasticsearchServiceMock.createErrorTransportRequestPromise(error2); + } + return elasticsearchServiceMock.createSuccessTransportRequestPromise({ + license: buildRawLicense(), + features: {}, + }); + }); const coreSetup = createCoreSetupWith(esClient); await plugin.setup(coreSetup); const { license$ } = await plugin.start(); const [first, second, third] = await license$.pipe(take(3), toArray()).toPromise(); + + // console.log('***', first); + expect(first.error).toBe(error1.message); expect(second.error).toBe(error2.message); expect(third.type).toBe('basic'); }); it('fetch license immediately without subscriptions', async () => { - const esClient = elasticsearchServiceMock.createLegacyClusterClient(); - esClient.callAsInternalUser.mockResolvedValue({ + const esClient = createEsClient({ license: buildRawLicense(), features: {}, }); @@ -184,12 +198,11 @@ describe('licensing plugin', () => { await flushPromises(); - expect(esClient.callAsInternalUser).toHaveBeenCalledTimes(1); + expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(1); }); it('logs license details without subscriptions', async () => { - const esClient = elasticsearchServiceMock.createLegacyClusterClient(); - esClient.callAsInternalUser.mockResolvedValue({ + const esClient = createEsClient({ license: buildRawLicense(), features: {}, }); @@ -214,13 +227,13 @@ describe('licensing plugin', () => { it('generates signature based on fetched license content', async () => { const types: LicenseType[] = ['basic', 'gold', 'basic']; - const esClient = elasticsearchServiceMock.createLegacyClusterClient(); - esClient.callAsInternalUser.mockImplementation(() => - Promise.resolve({ + const esClient = createEsClient(); + esClient.asInternalUser.xpack.info.mockImplementation(() => { + return elasticsearchServiceMock.createSuccessTransportRequestPromise({ license: buildRawLicense({ type: types.shift() }), features: {}, - }) - ); + }); + }); const coreSetup = createCoreSetupWith(esClient); await plugin.setup(coreSetup); @@ -245,8 +258,7 @@ describe('licensing plugin', () => { api_polling_frequency: moment.duration(50000), }) ); - const esClient = elasticsearchServiceMock.createLegacyClusterClient(); - esClient.callAsInternalUser.mockResolvedValue({ + const esClient = createEsClient({ license: buildRawLicense(), features: {}, }); @@ -255,14 +267,14 @@ describe('licensing plugin', () => { await plugin.setup(coreSetup); const { refresh, license$ } = await plugin.start(); - expect(esClient.callAsInternalUser).toHaveBeenCalledTimes(0); + expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(0); await license$.pipe(take(1)).toPromise(); - expect(esClient.callAsInternalUser).toHaveBeenCalledTimes(1); + expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(1); refresh(); await flushPromises(); - expect(esClient.callAsInternalUser).toHaveBeenCalledTimes(2); + expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(2); }); }); @@ -280,8 +292,7 @@ describe('licensing plugin', () => { }) ); - const esClient = elasticsearchServiceMock.createLegacyClusterClient(); - esClient.callAsInternalUser.mockResolvedValue({ + const esClient = createEsClient({ license: buildRawLicense(), features: {}, }); @@ -289,8 +300,7 @@ describe('licensing plugin', () => { await plugin.setup(coreSetup); const { createLicensePoller, license$ } = await plugin.start(); - const customClient = elasticsearchServiceMock.createLegacyClusterClient(); - customClient.callAsInternalUser.mockResolvedValue({ + const customClient = createEsClient({ license: buildRawLicense({ type: 'gold' }), features: {}, }); @@ -300,10 +310,10 @@ describe('licensing plugin', () => { customClient, customPollingFrequency ); - expect(customClient.callAsInternalUser).toHaveBeenCalledTimes(0); + expect(customClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(0); const customLicense = await customLicense$.pipe(take(1)).toPromise(); - expect(customClient.callAsInternalUser).toHaveBeenCalledTimes(1); + expect(customClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(1); await flushPromises(customPollingFrequency * 1.5); @@ -324,18 +334,17 @@ describe('licensing plugin', () => { await plugin.setup(coreSetup); const { createLicensePoller } = await plugin.start(); - const customClient = elasticsearchServiceMock.createLegacyClusterClient(); - customClient.callAsInternalUser.mockResolvedValue({ + const customClient = createEsClient({ license: buildRawLicense({ type: 'gold' }), features: {}, }); const { license$, refresh } = createLicensePoller(customClient, 10000); - expect(customClient.callAsInternalUser).toHaveBeenCalledTimes(0); + expect(customClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(0); await refresh(); - expect(customClient.callAsInternalUser).toHaveBeenCalledTimes(1); + expect(customClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(1); const license = await license$.pipe(take(1)).toPromise(); expect(license.type).toBe('gold'); }); From b4fbe808f0dbbd5f71e40c60eb90c5e21b55b445 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Mon, 22 Feb 2021 15:11:08 +0100 Subject: [PATCH 04/11] Revert "add logs" This reverts commit 4a61b646 --- src/core/server/status/status_service.ts | 28 ++++++++--------------- x-pack/plugins/licensing/server/plugin.ts | 24 +------------------ 2 files changed, 10 insertions(+), 42 deletions(-) diff --git a/src/core/server/status/status_service.ts b/src/core/server/status/status_service.ts index 95fb760d2a2405..09cf5b92b2b8a3 100644 --- a/src/core/server/status/status_service.ts +++ b/src/core/server/status/status_service.ts @@ -7,15 +7,7 @@ */ import { Observable, combineLatest, Subscription } from 'rxjs'; -import { - map, - distinctUntilChanged, - shareReplay, - take, - debounceTime, - pairwise, - startWith, -} from 'rxjs/operators'; +import { map, distinctUntilChanged, shareReplay, take, debounceTime } from 'rxjs/operators'; import { isDeepStrictEqual } from 'util'; import { CoreService } from '../../types'; @@ -47,7 +39,6 @@ export class StatusService implements CoreService { private readonly logger: Logger; private readonly config$: Observable; - private overall$?: Observable; private pluginsStatus?: PluginsStatusService; private overallSubscription?: Subscription; @@ -68,7 +59,10 @@ export class StatusService implements CoreService { const core$ = this.setupCoreStatus({ elasticsearch, savedObjects }); this.pluginsStatus = new PluginsStatusService({ core$, pluginDependencies }); - this.overall$ = combineLatest([core$, this.pluginsStatus.getAll$()]).pipe( + const overall$: Observable = combineLatest([ + core$, + this.pluginsStatus.getAll$(), + ]).pipe( // Prevent many emissions at once from dependency status resolution from making this too noisy debounceTime(500), map(([coreStatus, pluginsStatus]) => { @@ -84,7 +78,7 @@ export class StatusService implements CoreService { ); // Create an unused subscription to ensure all underlying lazy observables are started. - this.overallSubscription = this.overall$.subscribe(); + this.overallSubscription = overall$.subscribe(); const router = http.createRouter(''); registerStatusRoute({ @@ -97,7 +91,7 @@ export class StatusService implements CoreService { }, metrics, status: { - overall$: this.overall$, + overall$, plugins$: this.pluginsStatus.getAll$(), core$, }, @@ -105,7 +99,7 @@ export class StatusService implements CoreService { return { core$, - overall$: this.overall$, + overall$, plugins: { set: this.pluginsStatus.set.bind(this.pluginsStatus), getDependenciesStatus$: this.pluginsStatus.getDependenciesStatus$.bind(this.pluginsStatus), @@ -115,11 +109,7 @@ export class StatusService implements CoreService { }; } - public start() { - this.overall$!.pipe(startWith(undefined), pairwise()).subscribe(([oldStatus, newStatus]) => { - this.logger.info(`Kibana overall status is now ${newStatus!.level.toString()}`); - }); - } + public start() {} public stop() { if (this.overallSubscription) { diff --git a/x-pack/plugins/licensing/server/plugin.ts b/x-pack/plugins/licensing/server/plugin.ts index 3e8976a27c3327..082b3508a3ce3f 100644 --- a/x-pack/plugins/licensing/server/plugin.ts +++ b/x-pack/plugins/licensing/server/plugin.ts @@ -6,7 +6,6 @@ */ import { Observable, Subject, Subscription, timer } from 'rxjs'; -import { take, startWith, map } from 'rxjs/operators'; import moment from 'moment'; import { createHash } from 'crypto'; import stringify from 'json-stable-stringify'; @@ -19,8 +18,7 @@ import { Plugin, PluginInitializerContext, IClusterClient, - ServiceStatusLevels, -} from '../../../../src/core/server'; +} from 'src/core/server'; import { ILicense, PublicLicense, PublicFeatures } from '../common/types'; import { LicensingPluginSetup, LicensingPluginStart } from './types'; @@ -109,26 +107,6 @@ export class LicensingPlugin implements Plugin { - if (license) { - return { - level: ServiceStatusLevels.available, - summary: 'License fetched', - }; - } else { - return { - level: ServiceStatusLevels.degraded, - summary: 'license not fetched yet', - }; - } - }) - ) - ); - core.http.registerRouteHandlerContext( 'licensing', createRouteHandlerContext(license$, core.getStartServices) From f131b0cabeb4bb1958b82918ab80ca71df26e74a Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 23 Jun 2021 11:13:22 +0200 Subject: [PATCH 05/11] fix some type errors --- .../plugins/licensing/server/plugin.test.ts | 7 ++-- x-pack/plugins/licensing/server/plugin.ts | 36 +++++++++++++------ x-pack/plugins/licensing/server/types.ts | 22 +----------- 3 files changed, 31 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/licensing/server/plugin.test.ts b/x-pack/plugins/licensing/server/plugin.test.ts index ff358353647504..98f536bb85d0ef 100644 --- a/x-pack/plugins/licensing/server/plugin.test.ts +++ b/x-pack/plugins/licensing/server/plugin.test.ts @@ -6,9 +6,10 @@ */ import { take, toArray } from 'rxjs/operators'; +import { estypes } from '@elastic/elasticsearch'; import moment from 'moment'; import { LicenseType } from '../common/types'; -import { ElasticsearchError, RawLicense } from './types'; +import { ElasticsearchError } from './types'; import { LicensingPlugin } from './plugin'; import { coreMock, @@ -17,7 +18,9 @@ import { } from '../../../../src/core/server/mocks'; import { IClusterClient } from '../../../../src/core/server'; -function buildRawLicense(options: Partial = {}): RawLicense { +function buildRawLicense( + options: Partial = {} +): estypes.XpackInfoMinimalLicenseInformation { return { uid: 'uid-000000001234', status: 'active', diff --git a/x-pack/plugins/licensing/server/plugin.ts b/x-pack/plugins/licensing/server/plugin.ts index a3e1de1c1f233e..c2375f50f0a9b5 100644 --- a/x-pack/plugins/licensing/server/plugin.ts +++ b/x-pack/plugins/licensing/server/plugin.ts @@ -10,6 +10,7 @@ import moment from 'moment'; import { createHash } from 'crypto'; import stringify from 'json-stable-stringify'; +import { estypes } from '@elastic/elasticsearch'; import { MaybePromise } from '@kbn/utility-types'; import { isPromise } from '@kbn/std'; import { @@ -20,12 +21,18 @@ import { IClusterClient, } from 'src/core/server'; -import { ILicense, PublicLicense, PublicFeatures } from '../common/types'; +import { + ILicense, + PublicLicense, + PublicFeatures, + LicenseType, + LicenseStatus, +} from '../common/types'; import { LicensingPluginSetup, LicensingPluginStart } from './types'; import { License } from '../common/license'; import { createLicenseUpdate } from '../common/license_update'; -import { ElasticsearchError, RawLicense, RawFeatures } from './types'; +import { ElasticsearchError } from './types'; import { registerRoutes } from './routes'; import { FeatureUsageService } from './services'; @@ -34,17 +41,22 @@ import { createRouteHandlerContext } from './licensing_route_handler_context'; import { createOnPreResponseHandler } from './on_pre_response_handler'; import { getPluginStatus$ } from './plugin_status'; -function normalizeServerLicense(license: RawLicense): PublicLicense { +function normalizeServerLicense( + license: estypes.XpackInfoMinimalLicenseInformation +): PublicLicense { return { uid: license.uid, - type: license.type, - mode: license.mode, - expiryDateInMillis: license.expiry_date_in_millis, - status: license.status, + type: license.type as LicenseType, + mode: license.mode as LicenseType, + expiryDateInMillis: + typeof license.expiry_date_in_millis === 'string' + ? parseInt(license.expiry_date_in_millis, 10) + : license.expiry_date_in_millis, + status: license.status as LicenseStatus, }; } -function normalizeFeatures(rawFeatures: RawFeatures) { +function normalizeFeatures(rawFeatures: estypes.XpackInfoFeatures) { const features: PublicFeatures = {}; for (const [name, feature] of Object.entries(rawFeatures)) { features[name] = { @@ -167,11 +179,13 @@ export class LicensingPlugin implements Plugin Date: Wed, 23 Jun 2021 11:19:15 +0200 Subject: [PATCH 06/11] fix test types --- x-pack/plugins/licensing/server/plugin.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/licensing/server/plugin.test.ts b/x-pack/plugins/licensing/server/plugin.test.ts index 98f536bb85d0ef..9953fea38e440c 100644 --- a/x-pack/plugins/licensing/server/plugin.test.ts +++ b/x-pack/plugins/licensing/server/plugin.test.ts @@ -55,7 +55,7 @@ describe('licensing plugin', () => { const client = elasticsearchServiceMock.createClusterClient(); if (response) { client.asInternalUser.xpack.info.mockReturnValue( - elasticsearchServiceMock.createSuccessTransportRequestPromise(response) + elasticsearchServiceMock.createSuccessTransportRequestPromise(response as any) ); } return client; @@ -115,7 +115,7 @@ describe('licensing plugin', () => { return elasticsearchServiceMock.createSuccessTransportRequestPromise({ license: buildRawLicense({ type: types.shift() }), features: {}, - }); + } as estypes.XpackInfoResponse); }); const coreSetup = createCoreSetupWith(esClient); @@ -173,7 +173,7 @@ describe('licensing plugin', () => { return elasticsearchServiceMock.createSuccessTransportRequestPromise({ license: buildRawLicense(), features: {}, - }); + } as estypes.XpackInfoResponse); }); const coreSetup = createCoreSetupWith(esClient); @@ -235,7 +235,7 @@ describe('licensing plugin', () => { return elasticsearchServiceMock.createSuccessTransportRequestPromise({ license: buildRawLicense({ type: types.shift() }), features: {}, - }); + } as estypes.XpackInfoResponse); }); const coreSetup = createCoreSetupWith(esClient); From d755f1ba2972160f353c6b16f0d0e18e2ce58e44 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 23 Jun 2021 12:07:57 +0200 Subject: [PATCH 07/11] adapt monitoring usage of `createLicensePoller` --- .../es_client/instantiate_client.test.js | 14 +++++----- .../server/es_client/instantiate_client.ts | 19 +++++++++++-- .../monitoring/server/license_service.ts | 4 +-- x-pack/plugins/monitoring/server/plugin.ts | 27 +++++++++++-------- 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/monitoring/server/es_client/instantiate_client.test.js b/x-pack/plugins/monitoring/server/es_client/instantiate_client.test.js index bfe72ac1ead075..2990d61d3d3286 100644 --- a/x-pack/plugins/monitoring/server/es_client/instantiate_client.test.js +++ b/x-pack/plugins/monitoring/server/es_client/instantiate_client.test.js @@ -5,7 +5,7 @@ * 2.0. */ -import { instantiateClient, hasMonitoringCluster } from './instantiate_client'; +import { instantiateLegacyClient, hasMonitoringCluster } from './instantiate_client'; const server = { monitoring: { @@ -49,13 +49,13 @@ describe('Instantiate Client', () => { describe('Logging', () => { it('logs that the config was sourced from the production options', () => { - instantiateClient(server.monitoring.ui.elasticsearch, log, createClient); + instantiateLegacyClient(server.monitoring.ui.elasticsearch, log, createClient); expect(log.info.mock.calls[0]).toEqual(['config sourced from: production cluster']); }); it('logs that the config was sourced from the monitoring options', () => { - instantiateClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); + instantiateLegacyClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); expect(log.info.mock.calls[0]).toEqual(['config sourced from: monitoring cluster']); }); @@ -63,7 +63,7 @@ describe('Instantiate Client', () => { describe('Custom Headers Configuration', () => { it('Does not add xpack.monitoring.elasticsearch.customHeaders if connected to production cluster', () => { - instantiateClient(server.monitoring.ui.elasticsearch, log, createClient); + instantiateLegacyClient(server.monitoring.ui.elasticsearch, log, createClient); const createClusterCall = createClient.mock.calls[0]; expect(createClient).toHaveBeenCalledTimes(1); @@ -72,7 +72,7 @@ describe('Instantiate Client', () => { }); it('Adds xpack.monitoring.elasticsearch.customHeaders if connected to monitoring cluster', () => { - instantiateClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); + instantiateLegacyClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); const createClusterCall = createClient.mock.calls[0]; @@ -86,7 +86,7 @@ describe('Instantiate Client', () => { describe('Use a connection to production cluster', () => { it('exposes an authenticated client using production host settings', () => { - instantiateClient(server.monitoring.ui.elasticsearch, log, createClient); + instantiateLegacyClient(server.monitoring.ui.elasticsearch, log, createClient); const createClusterCall = createClient.mock.calls[0]; const createClientOptions = createClusterCall[1]; @@ -99,7 +99,7 @@ describe('Instantiate Client', () => { describe('Use a connection to monitoring cluster', () => { it('exposes an authenticated client using monitoring host settings', () => { - instantiateClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); + instantiateLegacyClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); const createClusterCall = createClient.mock.calls[0]; const createClientOptions = createClusterCall[1]; diff --git a/x-pack/plugins/monitoring/server/es_client/instantiate_client.ts b/x-pack/plugins/monitoring/server/es_client/instantiate_client.ts index a32943dc10272a..8e95286ea6c5e9 100644 --- a/x-pack/plugins/monitoring/server/es_client/instantiate_client.ts +++ b/x-pack/plugins/monitoring/server/es_client/instantiate_client.ts @@ -6,7 +6,7 @@ */ import { ConfigOptions } from 'elasticsearch'; -import { Logger, ILegacyCustomClusterClient } from 'kibana/server'; +import { Logger, ILegacyCustomClusterClient, ICustomClusterClient } from 'kibana/server'; // @ts-ignore import { monitoringBulk } from '../kibana_monitoring/lib/monitoring_bulk'; import { monitoringEndpointDisableWatches } from './monitoring_endpoint_disable_watches'; @@ -20,7 +20,7 @@ import { MonitoringElasticsearchConfig } from '../config'; type ESClusterConfig = MonitoringElasticsearchConfig & Pick; -export function instantiateClient( +export function instantiateLegacyClient( elasticsearchConfig: MonitoringElasticsearchConfig, log: Logger, createClient: ( @@ -39,6 +39,21 @@ export function instantiateClient( return cluster; } +export function instantiateClient( + elasticsearchConfig: MonitoringElasticsearchConfig, + log: Logger, + createClient: (type: string, clientConfig?: Partial) => ICustomClusterClient +) { + const isMonitoringCluster = hasMonitoringCluster(elasticsearchConfig); + const cluster = createClient('monitoring', { + ...(isMonitoringCluster ? elasticsearchConfig : {}), + }); + + const configSource = isMonitoringCluster ? 'monitoring' : 'production'; + log.info(`config sourced from: ${configSource} cluster`); + return cluster; +} + export function hasMonitoringCluster(config: MonitoringElasticsearchConfig) { return Boolean(config.hosts && config.hosts[0]); } diff --git a/x-pack/plugins/monitoring/server/license_service.ts b/x-pack/plugins/monitoring/server/license_service.ts index fde22aaefa5dd2..0ac1c9d3498b17 100644 --- a/x-pack/plugins/monitoring/server/license_service.ts +++ b/x-pack/plugins/monitoring/server/license_service.ts @@ -6,7 +6,7 @@ */ import { Subscription } from 'rxjs'; -import { ILegacyCustomClusterClient } from 'kibana/server'; +import { ICustomClusterClient } from 'kibana/server'; import { ILicense, LicenseFeature } from '../../licensing/common/types'; import { LicensingPluginStart } from '../../licensing/server'; import { MonitoringConfig } from './config'; @@ -15,7 +15,7 @@ import { MonitoringLicenseService } from './types'; interface SetupDeps { licensing: LicensingPluginStart; - monitoringClient: ILegacyCustomClusterClient; + monitoringClient: ICustomClusterClient; config: MonitoringConfig; log: Logger; } diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index 10724594ce576d..34a5984d15bfbd 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -34,7 +34,7 @@ import { requireUIRoutes } from './routes'; import { initBulkUploader } from './kibana_monitoring'; import { initInfraSource } from './lib/logs/init_infra_source'; import { mbSafeQuery } from './lib/mb_safe_query'; -import { instantiateClient } from './es_client/instantiate_client'; +import { instantiateClient, instantiateLegacyClient } from './es_client/instantiate_client'; import { registerCollectors } from './kibana_monitoring/collectors'; import { registerMonitoringTelemetryCollection } from './telemetry_collection'; import { LicenseService } from './license_service'; @@ -71,7 +71,7 @@ export class MonitoringPlugin private readonly initializerContext: PluginInitializerContext; private readonly log: Logger; private readonly getLogger: (...scopes: string[]) => Logger; - private cluster = {} as ILegacyCustomClusterClient; + private legacyCluster = {} as ILegacyCustomClusterClient; private licenseService = {} as MonitoringLicenseService; private monitoringCore = {} as MonitoringCore; private legacyShimDependencies = {} as LegacyShimDependencies; @@ -99,13 +99,13 @@ export class MonitoringPlugin // Monitoring creates and maintains a connection to a potentially // separate ES cluster - create this first - const cluster = (this.cluster = instantiateClient( + const legacyCluster = (this.legacyCluster = instantiateLegacyClient( config.ui.elasticsearch, this.log, core.elasticsearch.legacy.createClient )); - Globals.init(core, plugins.cloud, cluster, config, this.getLogger); + Globals.init(core, plugins.cloud, legacyCluster, config, this.getLogger); const serverInfo = core.http.getServerInfo(); const alerts = AlertsFactory.getAll(); for (const alert of alerts) { @@ -127,10 +127,10 @@ export class MonitoringPlugin }, }); - registerCollectors(plugins.usageCollection, config, cluster); + registerCollectors(plugins.usageCollection, config, legacyCluster); registerMonitoringTelemetryCollection( plugins.usageCollection, - cluster, + legacyCluster, config.ui.max_bucket_size ); } @@ -163,13 +163,13 @@ export class MonitoringPlugin config, legacyConfig, core.getStartServices as () => Promise<[CoreStart, PluginsStart, {}]>, - this.cluster, + this.legacyCluster, plugins ); this.registerPluginInUI(plugins); requireUIRoutes(this.monitoringCore, { - cluster, + cluster: legacyCluster, router, licenseService: this.licenseService, encryptedSavedObjects: plugins.encryptedSavedObjects, @@ -189,9 +189,14 @@ export class MonitoringPlugin const config = createConfig(this.initializerContext.config.get>()); // Start our license service which will ensure // the appropriate licenses are present + const cluster = instantiateClient( + config.ui.elasticsearch, + this.log, + core.elasticsearch.createClient + ); this.licenseService = new LicenseService().setup({ licensing, - monitoringClient: this.cluster, + monitoringClient: cluster, config, log: this.log, }); @@ -227,8 +232,8 @@ export class MonitoringPlugin } stop() { - if (this.cluster) { - this.cluster.close(); + if (this.legacyCluster) { + this.legacyCluster.close(); } if (this.licenseService && this.licenseService.stop) { this.licenseService.stop(); From 42a119d9090277b2061b8f9968e6794e4da0f5ed Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 23 Jun 2021 12:13:00 +0200 Subject: [PATCH 08/11] remove test comment --- x-pack/plugins/licensing/server/plugin.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/x-pack/plugins/licensing/server/plugin.test.ts b/x-pack/plugins/licensing/server/plugin.test.ts index 9953fea38e440c..1fe4bbf238e197 100644 --- a/x-pack/plugins/licensing/server/plugin.test.ts +++ b/x-pack/plugins/licensing/server/plugin.test.ts @@ -182,8 +182,6 @@ describe('licensing plugin', () => { const [first, second, third] = await license$.pipe(take(3), toArray()).toPromise(); - // console.log('***', first); - expect(first.error).toBe(error1.message); expect(second.error).toBe(error2.message); expect(third.type).toBe('basic'); From fd524e1066e0aaeae2de61e4df3db432ae90bc99 Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 23 Jun 2021 14:50:22 +0200 Subject: [PATCH 09/11] fix unit test --- x-pack/plugins/monitoring/server/plugin.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/plugins/monitoring/server/plugin.test.ts b/x-pack/plugins/monitoring/server/plugin.test.ts index 1bdef23dd0c369..68453a849a15b2 100644 --- a/x-pack/plugins/monitoring/server/plugin.test.ts +++ b/x-pack/plugins/monitoring/server/plugin.test.ts @@ -13,6 +13,9 @@ jest.mock('./es_client/instantiate_client', () => ({ instantiateClient: jest.fn().mockImplementation(() => ({ cluster: {}, })), + instantiateLegacyClient: jest.fn().mockImplementation(() => ({ + cluster: {}, + })), })); jest.mock('./license_service', () => ({ From ceeb62dff1d575a3308ca28bb3648994d044c04e Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 30 Jun 2021 09:04:00 +0200 Subject: [PATCH 10/11] remove createLicensePoller from setup contract --- x-pack/plugins/licensing/server/mocks.ts | 5 ----- x-pack/plugins/licensing/server/plugin.ts | 1 - x-pack/plugins/licensing/server/types.ts | 10 ---------- 3 files changed, 16 deletions(-) diff --git a/x-pack/plugins/licensing/server/mocks.ts b/x-pack/plugins/licensing/server/mocks.ts index 186d35ec47dd34..724a5aa2d057e1 100644 --- a/x-pack/plugins/licensing/server/mocks.ts +++ b/x-pack/plugins/licensing/server/mocks.ts @@ -19,14 +19,9 @@ const createSetupMock = (): jest.Mocked => { const mock = { license$: new BehaviorSubject(license), refresh: jest.fn(), - createLicensePoller: jest.fn(), featureUsage: featureUsageMock.createSetup(), }; mock.refresh.mockResolvedValue(license); - mock.createLicensePoller.mockReturnValue({ - license$: mock.license$, - refresh: mock.refresh, - }); return mock; }; diff --git a/x-pack/plugins/licensing/server/plugin.ts b/x-pack/plugins/licensing/server/plugin.ts index c2375f50f0a9b5..00d2ae602fcae5 100644 --- a/x-pack/plugins/licensing/server/plugin.ts +++ b/x-pack/plugins/licensing/server/plugin.ts @@ -138,7 +138,6 @@ export class LicensingPlugin implements Plugin; - /** - * Creates a license poller to retrieve a license data with. - * Allows a plugin to configure a cluster to retrieve data from at - * given polling frequency. - * @deprecated in favour of the counterpart provided from start contract - */ - createLicensePoller: ( - clusterClient: IClusterClient, - pollingFrequency: number - ) => { license$: Observable; refresh(): Promise }; /** * APIs to register licensed feature usage. */ From d8b9355b8c414aca3a7848fbbe08ee1a158ac05a Mon Sep 17 00:00:00 2001 From: pgayvallet Date: Wed, 30 Jun 2021 12:35:40 +0200 Subject: [PATCH 11/11] fix unit tests --- .../server/es_client/instantiate_client.test.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/monitoring/server/es_client/instantiate_client.test.js b/x-pack/plugins/monitoring/server/es_client/instantiate_client.test.js index 2990d61d3d3286..bfe72ac1ead075 100644 --- a/x-pack/plugins/monitoring/server/es_client/instantiate_client.test.js +++ b/x-pack/plugins/monitoring/server/es_client/instantiate_client.test.js @@ -5,7 +5,7 @@ * 2.0. */ -import { instantiateLegacyClient, hasMonitoringCluster } from './instantiate_client'; +import { instantiateClient, hasMonitoringCluster } from './instantiate_client'; const server = { monitoring: { @@ -49,13 +49,13 @@ describe('Instantiate Client', () => { describe('Logging', () => { it('logs that the config was sourced from the production options', () => { - instantiateLegacyClient(server.monitoring.ui.elasticsearch, log, createClient); + instantiateClient(server.monitoring.ui.elasticsearch, log, createClient); expect(log.info.mock.calls[0]).toEqual(['config sourced from: production cluster']); }); it('logs that the config was sourced from the monitoring options', () => { - instantiateLegacyClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); + instantiateClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); expect(log.info.mock.calls[0]).toEqual(['config sourced from: monitoring cluster']); }); @@ -63,7 +63,7 @@ describe('Instantiate Client', () => { describe('Custom Headers Configuration', () => { it('Does not add xpack.monitoring.elasticsearch.customHeaders if connected to production cluster', () => { - instantiateLegacyClient(server.monitoring.ui.elasticsearch, log, createClient); + instantiateClient(server.monitoring.ui.elasticsearch, log, createClient); const createClusterCall = createClient.mock.calls[0]; expect(createClient).toHaveBeenCalledTimes(1); @@ -72,7 +72,7 @@ describe('Instantiate Client', () => { }); it('Adds xpack.monitoring.elasticsearch.customHeaders if connected to monitoring cluster', () => { - instantiateLegacyClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); + instantiateClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); const createClusterCall = createClient.mock.calls[0]; @@ -86,7 +86,7 @@ describe('Instantiate Client', () => { describe('Use a connection to production cluster', () => { it('exposes an authenticated client using production host settings', () => { - instantiateLegacyClient(server.monitoring.ui.elasticsearch, log, createClient); + instantiateClient(server.monitoring.ui.elasticsearch, log, createClient); const createClusterCall = createClient.mock.calls[0]; const createClientOptions = createClusterCall[1]; @@ -99,7 +99,7 @@ describe('Instantiate Client', () => { describe('Use a connection to monitoring cluster', () => { it('exposes an authenticated client using monitoring host settings', () => { - instantiateLegacyClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); + instantiateClient(serverWithUrl.monitoring.ui.elasticsearch, log, createClient); const createClusterCall = createClient.mock.calls[0]; const createClientOptions = createClusterCall[1];