From 944c541c0c68dd539afedc34ab7e69fb82d25925 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 14 May 2021 17:10:55 +0200 Subject: [PATCH 01/16] wip; added logic for creating ILM policy at start up --- .../server/lib/store/report_ilm_policy.ts | 26 +++++++ .../reporting/server/lib/store/store.ts | 67 +++++++++++++++---- x-pack/plugins/reporting/server/plugin.ts | 3 + 3 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 x-pack/plugins/reporting/server/lib/store/report_ilm_policy.ts diff --git a/x-pack/plugins/reporting/server/lib/store/report_ilm_policy.ts b/x-pack/plugins/reporting/server/lib/store/report_ilm_policy.ts new file mode 100644 index 0000000000000..ff89ef26d890d --- /dev/null +++ b/x-pack/plugins/reporting/server/lib/store/report_ilm_policy.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PutLifecycleRequest } from '@elastic/elasticsearch/api/types'; + +// TODO: Review the default/starting policy +export const reportingIlmPolicy: PutLifecycleRequest['body'] = { + policy: { + phases: { + hot: { + actions: {}, + min_age: '0ms', + }, + delete: { + min_age: '180d', + actions: { + delete: {}, + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/reporting/server/lib/store/store.ts b/x-pack/plugins/reporting/server/lib/store/store.ts index fc7bd9c23d769..4c53f89816dce 100644 --- a/x-pack/plugins/reporting/server/lib/store/store.ts +++ b/x-pack/plugins/reporting/server/lib/store/store.ts @@ -14,6 +14,7 @@ import { ReportTaskParams } from '../tasks'; import { indexTimestamp } from './index_timestamp'; import { mapping } from './mapping'; import { Report, ReportDocument, ReportSource } from './report'; +import { reportingIlmPolicy } from './report_ilm_policy'; /* * When searching for long-pending reports, we get a subset of fields @@ -71,19 +72,22 @@ export class ReportingStore { return exists; } - const indexSettings = { - number_of_shards: 1, - auto_expand_replicas: '0-1', - }; - const body = { - settings: indexSettings, - mappings: { - properties: mapping, - }, - }; - try { - await client.indices.create({ index: indexName, body }); + await client.indices.create({ + index: indexName, + body: { + settings: { + number_of_shards: 1, + auto_expand_replicas: '0-1', + lifecycle: { + name: this.ilmPolicyName, + }, + }, + mappings: { + properties: mapping, + }, + }, + }); return true; } catch (error) { @@ -130,6 +134,45 @@ export class ReportingStore { return client.indices.refresh({ index }); } + private readonly ilmPolicyName = 'kibana-reporting'; + + private async doesIlmPolicyExist(): Promise { + const client = await this.getClient(); + try { + await client.ilm.getLifecycle({ policy: this.ilmPolicyName }); + return true; + } catch (e) { + if (e.statusCode === 404) { + return false; + } + throw e; + } + } + + /** + * Function to be called during plugin start phase. This ensures the environment is correctly + * configured for storage of reports. + */ + public async start() { + const client = await this.getClient(); + try { + if (await this.doesIlmPolicyExist()) { + return; + } + this.logger.debug( + `Creating ILM policy for managing reporting indices: ${this.ilmPolicyName}` + ); + await client.ilm.putLifecycle({ + policy: this.ilmPolicyName, + body: reportingIlmPolicy, + }); + } catch (e) { + this.logger.error('Error in start phase'); + this.logger.error(e.body.error); + throw e; + } + } + public async addReport(report: Report): Promise { let index = report._index; if (!index) { diff --git a/x-pack/plugins/reporting/server/plugin.ts b/x-pack/plugins/reporting/server/plugin.ts index 26a9be2b15c3f..e0ac39ed45b68 100644 --- a/x-pack/plugins/reporting/server/plugin.ts +++ b/x-pack/plugins/reporting/server/plugin.ts @@ -107,6 +107,9 @@ export class ReportingPlugin logger: this.logger, }); + // Note: this must be called after ReportingCore.pluginStart + await store.start(); + this.logger.debug('Start complete'); })().catch((e) => { this.logger.error(`Error in Reporting start, reporting may not function properly`); From c94974febe5da4093cf547f0c3d203da7fc8d482 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 17 May 2021 12:04:10 +0200 Subject: [PATCH 02/16] added log when ilm policy is not created --- x-pack/plugins/reporting/server/lib/store/store.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/reporting/server/lib/store/store.ts b/x-pack/plugins/reporting/server/lib/store/store.ts index 4c53f89816dce..b6a5d3dfe94cc 100644 --- a/x-pack/plugins/reporting/server/lib/store/store.ts +++ b/x-pack/plugins/reporting/server/lib/store/store.ts @@ -157,6 +157,7 @@ export class ReportingStore { const client = await this.getClient(); try { if (await this.doesIlmPolicyExist()) { + this.logger.debug(`Found ILM policy ${this.ilmPolicyName}; skipping creation.`); return; } this.logger.debug( From cc739ba867e67f48313202ec5d173860ac80e926 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 17 May 2021 12:25:26 +0200 Subject: [PATCH 03/16] added test for start function --- .../reporting/server/lib/store/store.test.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/x-pack/plugins/reporting/server/lib/store/store.test.ts b/x-pack/plugins/reporting/server/lib/store/store.test.ts index 7f96433fcc6ce..52471a105bd4f 100644 --- a/x-pack/plugins/reporting/server/lib/store/store.test.ts +++ b/x-pack/plugins/reporting/server/lib/store/store.test.ts @@ -7,6 +7,7 @@ import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; import { ElasticsearchClient } from 'src/core/server'; +import { elasticsearchServiceMock } from 'src/core/server/mocks'; import { ReportingCore } from '../../'; import { createMockConfigSchema, @@ -15,6 +16,9 @@ import { } from '../../test_helpers'; import { Report, ReportDocument } from './report'; import { ReportingStore } from './store'; +import { reportingIlmPolicy } from './report_ilm_policy'; + +const { createApiResponse } = elasticsearchServiceMock; describe('ReportingStore', () => { const mockLogger = createMockLevelLogger(); @@ -403,4 +407,47 @@ describe('ReportingStore', () => { ] `); }); + + describe('start', () => { + it('creates an ILM policy for managing reporting indices if there is not already one', async () => { + mockEsClient.ilm.getLifecycle.mockRejectedValueOnce(createApiResponse({ statusCode: 404 })); + mockEsClient.ilm.putLifecycle.mockResolvedValueOnce(createApiResponse()); + + const store = new ReportingStore(mockCore, mockLogger); + await store.start(); + + expect(mockEsClient.ilm.getLifecycle).toHaveBeenCalledWith({ policy: 'kibana-reporting' }); + expect(mockEsClient.ilm.putLifecycle.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "body": Object { + "policy": Object { + "phases": Object { + "delete": Object { + "actions": Object { + "delete": Object {}, + }, + "min_age": "180d", + }, + "hot": Object { + "actions": Object {}, + "min_age": "0ms", + }, + }, + }, + }, + "policy": "kibana-reporting", + } + `); + }); + + it('does not create an ILM policy for managing reporting indices if one already exists', async () => { + mockEsClient.ilm.getLifecycle.mockResolvedValueOnce(createApiResponse()); + + const store = new ReportingStore(mockCore, mockLogger); + await store.start(); + + expect(mockEsClient.ilm.getLifecycle).toHaveBeenCalledWith({ policy: 'kibana-reporting' }); + expect(mockEsClient.ilm.putLifecycle).not.toHaveBeenCalled(); + }); + }); }); From e9a27f7ad516eeb7abcddb7146a71922c7585988 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 17 May 2021 13:37:33 +0200 Subject: [PATCH 04/16] updated ilm policy to not delete data --- .../plugins/reporting/server/lib/store/report_ilm_policy.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/x-pack/plugins/reporting/server/lib/store/report_ilm_policy.ts b/x-pack/plugins/reporting/server/lib/store/report_ilm_policy.ts index ff89ef26d890d..9b37d3e293cf0 100644 --- a/x-pack/plugins/reporting/server/lib/store/report_ilm_policy.ts +++ b/x-pack/plugins/reporting/server/lib/store/report_ilm_policy.ts @@ -15,12 +15,6 @@ export const reportingIlmPolicy: PutLifecycleRequest['body'] = { actions: {}, min_age: '0ms', }, - delete: { - min_age: '180d', - actions: { - delete: {}, - }, - }, }, }, }; From 385e1700b6447eaa0923b1597bef4a2e2bc79cb2 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 17 May 2021 13:39:33 +0200 Subject: [PATCH 05/16] actually update jest snapshots and remove unused import --- x-pack/plugins/reporting/server/lib/store/store.test.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/x-pack/plugins/reporting/server/lib/store/store.test.ts b/x-pack/plugins/reporting/server/lib/store/store.test.ts index 52471a105bd4f..0f00c9279d0bf 100644 --- a/x-pack/plugins/reporting/server/lib/store/store.test.ts +++ b/x-pack/plugins/reporting/server/lib/store/store.test.ts @@ -16,7 +16,6 @@ import { } from '../../test_helpers'; import { Report, ReportDocument } from './report'; import { ReportingStore } from './store'; -import { reportingIlmPolicy } from './report_ilm_policy'; const { createApiResponse } = elasticsearchServiceMock; @@ -422,12 +421,6 @@ describe('ReportingStore', () => { "body": Object { "policy": Object { "phases": Object { - "delete": Object { - "actions": Object { - "delete": Object {}, - }, - "min_age": "180d", - }, "hot": Object { "actions": Object {}, "min_age": "0ms", From bcabc5c143277dd46927e45fce11f37f65f4ee25 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 18 May 2021 16:18:12 +0200 Subject: [PATCH 06/16] updated the ilm policy, removed the min_age for the hot phase --- x-pack/plugins/reporting/server/lib/store/report_ilm_policy.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/reporting/server/lib/store/report_ilm_policy.ts b/x-pack/plugins/reporting/server/lib/store/report_ilm_policy.ts index 9b37d3e293cf0..7e5718a76e7ca 100644 --- a/x-pack/plugins/reporting/server/lib/store/report_ilm_policy.ts +++ b/x-pack/plugins/reporting/server/lib/store/report_ilm_policy.ts @@ -13,7 +13,6 @@ export const reportingIlmPolicy: PutLifecycleRequest['body'] = { phases: { hot: { actions: {}, - min_age: '0ms', }, }, }, From 37aa7fd7f0fdac6c0b939b3701ef61350b381c15 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 18 May 2021 16:19:32 +0200 Subject: [PATCH 07/16] wip: integrate reporting with upgrade assistant --- .../reporting/server/deprecations/index.ts | 26 +++++++++ .../migrate_existing_indices_ilm_policy.ts | 57 +++++++++++++++++++ .../reporting/server/lib/store/store.ts | 11 +++- x-pack/plugins/reporting/server/plugin.ts | 5 ++ 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/reporting/server/deprecations/index.ts create mode 100644 x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts diff --git a/x-pack/plugins/reporting/server/deprecations/index.ts b/x-pack/plugins/reporting/server/deprecations/index.ts new file mode 100644 index 0000000000000..03d900b1b2e28 --- /dev/null +++ b/x-pack/plugins/reporting/server/deprecations/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { CoreSetup } from 'src/core/server'; +import { ReportingCore } from '../core'; + +import { migrateExistingIndicesIlmPolicy } from './migrate_existing_indices_ilm_policy'; + +export const registerDeprecations = ({ + core, + reportingCore, +}: { + core: CoreSetup; + reportingCore: ReportingCore; +}) => { + core.deprecations.registerDeprecations({ + getDeprecations(ctx) { + return migrateExistingIndicesIlmPolicy(ctx, { + reportingCore, + }); + }, + }); +}; diff --git a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts new file mode 100644 index 0000000000000..138f8113eaab3 --- /dev/null +++ b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { DeprecationsDetails, GetDeprecationsContext } from 'src/core/server'; +import { ReportingCore } from '../core'; + +interface ExtraDependencies { + reportingCore: ReportingCore; +} + +export const migrateExistingIndicesIlmPolicy = async ( + { esClient }: GetDeprecationsContext, + { reportingCore }: ExtraDependencies +): Promise => { + const store = await reportingCore.getStore(); + const reportingIlmPolicy = store.getIlmPolicyName(); + const reportingIndexPrefix = store.getIndexPrefix(); + + const { body: reportingIndicesSettings } = await esClient.asInternalUser.indices.getSettings({ + index: `${store.getIndexPrefix()}-*`, + }); + + const someIndicesNotManagedByReportingIlm = Object.values(reportingIndicesSettings).some( + (settings) => settings?.settings?.index?.lifecycle?.name !== reportingIlmPolicy + ); + + if (someIndicesNotManagedByReportingIlm) { + return [ + { + level: 'warning', + message: i18n.translate('xpack.reporting.deprecations.migrateIndexIlmPolicyActionMessage', { + defaultMessage: `Reporting indices can be managed by a provisioned ILM policy, ${reportingIlmPolicy}. This policy should be used to manage the lifecycle of indices. Please note, this action will target all indices prefixed with "${reportingIndexPrefix}-*".`, + }), + correctiveActions: { + api: { + method: 'PUT', + path: `${reportingIndexPrefix}-*/_settings`, + body: { + index: { + lifecycle: { + name: reportingIlmPolicy, + }, + }, + }, + }, + }, + }, + ]; + } + + return []; +}; diff --git a/x-pack/plugins/reporting/server/lib/store/store.ts b/x-pack/plugins/reporting/server/lib/store/store.ts index b6a5d3dfe94cc..c7fd7d85841f8 100644 --- a/x-pack/plugins/reporting/server/lib/store/store.ts +++ b/x-pack/plugins/reporting/server/lib/store/store.ts @@ -42,6 +42,7 @@ const checkReportIsEditable = (report: Report) => { * - interface for downloading the report */ export class ReportingStore { + private readonly ilmPolicyName = 'kibana-reporting'; // name of the ILM policy for managing indices created by this service. private readonly indexPrefix: string; // config setting of index prefix in system index name private readonly indexInterval: string; // config setting of index prefix: how often to poll for pending work private readonly queueTimeoutMins: number; // config setting of queue timeout, rounded up to nearest minute @@ -134,8 +135,6 @@ export class ReportingStore { return client.indices.refresh({ index }); } - private readonly ilmPolicyName = 'kibana-reporting'; - private async doesIlmPolicyExist(): Promise { const client = await this.getClient(); try { @@ -402,4 +401,12 @@ export class ReportingStore { return body.hits?.hits as ReportRecordTimeout[]; } + + public getIlmPolicyName(): string { + return this.ilmPolicyName; + } + + public getIndexPrefix(): string { + return this.indexPrefix; + } } diff --git a/x-pack/plugins/reporting/server/plugin.ts b/x-pack/plugins/reporting/server/plugin.ts index e0ac39ed45b68..4445a46f2b51e 100644 --- a/x-pack/plugins/reporting/server/plugin.ts +++ b/x-pack/plugins/reporting/server/plugin.ts @@ -21,6 +21,7 @@ import type { ReportingStartDeps, } from './types'; import { registerReportingUsageCollector } from './usage'; +import { registerDeprecations } from './deprecations'; export class ReportingPlugin implements Plugin { @@ -66,6 +67,10 @@ export class ReportingPlugin registerReportingUsageCollector(reportingCore, plugins); registerRoutes(reportingCore, this.logger); + registerDeprecations({ + core, + reportingCore, + }); // async background setup (async () => { From d649f56f0d7f47d50ca3570ea069957248acfcb3 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 19 May 2021 11:39:07 +0200 Subject: [PATCH 08/16] initial version of upgrade assistant integration --- x-pack/plugins/reporting/common/constants.ts | 1 + .../migrate_existing_indices_ilm_policy.ts | 10 ++-- .../reporting/server/lib/store/store.ts | 4 +- .../reporting/server/routes/deprecations.ts | 50 +++++++++++++++++++ .../plugins/reporting/server/routes/index.ts | 6 ++- 5 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 x-pack/plugins/reporting/server/routes/deprecations.ts diff --git a/x-pack/plugins/reporting/common/constants.ts b/x-pack/plugins/reporting/common/constants.ts index 2a95557473fc0..54df24db49f29 100644 --- a/x-pack/plugins/reporting/common/constants.ts +++ b/x-pack/plugins/reporting/common/constants.ts @@ -89,6 +89,7 @@ export const API_BASE_URL = '/api/reporting'; // "Generation URL" from share men export const API_BASE_GENERATE = `${API_BASE_URL}/generate`; export const API_LIST_URL = `${API_BASE_URL}/jobs`; export const API_DIAGNOSE_URL = `${API_BASE_URL}/diagnose`; +export const API_MIGRATE_ILM_POLICY_URL = `${API_BASE_URL}/deprecations/migrate_ilm_policy`; // hacky endpoint: download CSV without queueing a report export const API_BASE_URL_V1 = '/api/reporting/v1'; // diff --git a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts index 138f8113eaab3..16bbfd50a0646 100644 --- a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts +++ b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { DeprecationsDetails, GetDeprecationsContext } from 'src/core/server'; +import { API_MIGRATE_ILM_POLICY_URL } from '../../common/constants'; import { ReportingCore } from '../core'; interface ExtraDependencies { @@ -19,10 +20,10 @@ export const migrateExistingIndicesIlmPolicy = async ( ): Promise => { const store = await reportingCore.getStore(); const reportingIlmPolicy = store.getIlmPolicyName(); - const reportingIndexPrefix = store.getIndexPrefix(); + const indexPattern = store.getReportingIndexPattern(); const { body: reportingIndicesSettings } = await esClient.asInternalUser.indices.getSettings({ - index: `${store.getIndexPrefix()}-*`, + index: indexPattern, }); const someIndicesNotManagedByReportingIlm = Object.values(reportingIndicesSettings).some( @@ -34,12 +35,13 @@ export const migrateExistingIndicesIlmPolicy = async ( { level: 'warning', message: i18n.translate('xpack.reporting.deprecations.migrateIndexIlmPolicyActionMessage', { - defaultMessage: `Reporting indices can be managed by a provisioned ILM policy, ${reportingIlmPolicy}. This policy should be used to manage the lifecycle of indices. Please note, this action will target all indices prefixed with "${reportingIndexPrefix}-*".`, + defaultMessage: `All new reporting indices will be managed by a provisioned ILM policy: "${reportingIlmPolicy}". To manage the lifecycle of reports edit the ${reportingIlmPolicy} policy. Please note, this action will target all indices prefixed with "${indexPattern}".`, }), + documentationUrl: 'WIP', // TODO: Fix this! correctiveActions: { api: { method: 'PUT', - path: `${reportingIndexPrefix}-*/_settings`, + path: API_MIGRATE_ILM_POLICY_URL, body: { index: { lifecycle: { diff --git a/x-pack/plugins/reporting/server/lib/store/store.ts b/x-pack/plugins/reporting/server/lib/store/store.ts index c7fd7d85841f8..e27881e133be0 100644 --- a/x-pack/plugins/reporting/server/lib/store/store.ts +++ b/x-pack/plugins/reporting/server/lib/store/store.ts @@ -406,7 +406,7 @@ export class ReportingStore { return this.ilmPolicyName; } - public getIndexPrefix(): string { - return this.indexPrefix; + public getReportingIndexPattern(): string { + return `${this.indexPrefix}-*`; } } diff --git a/x-pack/plugins/reporting/server/routes/deprecations.ts b/x-pack/plugins/reporting/server/routes/deprecations.ts new file mode 100644 index 0000000000000..4bd4f4ad04d28 --- /dev/null +++ b/x-pack/plugins/reporting/server/routes/deprecations.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { errors } from '@elastic/elasticsearch'; +import { API_MIGRATE_ILM_POLICY_URL } from '../../common/constants'; +import { ReportingCore } from '../core'; +import { LevelLogger as Logger } from '../lib'; + +export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Logger) => { + const { router } = reporting.getPluginSetupDeps(); + + router.put({ path: API_MIGRATE_ILM_POLICY_URL, validate: false }, async (ctx, req, res) => { + const store = await reporting.getStore(); + const { asInternalUser: client } = await reporting.getEsClient(); + + const indexPattern = store.getReportingIndexPattern(); + const reportingIlmPolicy = store.getIlmPolicyName(); + + try { + await client.indices.putSettings({ + index: indexPattern, + body: { + index: { + lifecycle: { + name: reportingIlmPolicy, + }, + }, + }, + }); + return res.ok(); + } catch (err) { + logger.error(err); + + if (err instanceof errors.ResponseError) { + return res.customError({ + statusCode: 500, + body: { + message: err.message, + name: err.name, + }, + }); + } + + throw err; + } + }); +}; diff --git a/x-pack/plugins/reporting/server/routes/index.ts b/x-pack/plugins/reporting/server/routes/index.ts index e061bd4f7d66c..a462da3849083 100644 --- a/x-pack/plugins/reporting/server/routes/index.ts +++ b/x-pack/plugins/reporting/server/routes/index.ts @@ -6,15 +6,17 @@ */ import { LevelLogger as Logger } from '../lib'; +import { registerDeprecationsRoutes } from './deprecations'; +import { registerDiagnosticRoutes } from './diagnostic'; import { registerJobGenerationRoutes } from './generation'; import { registerJobInfoRoutes } from './jobs'; import { ReportingCore } from '../core'; -import { registerDiagnosticRoutes } from './diagnostic'; export function registerRoutes(reporting: ReportingCore, logger: Logger) { + registerDeprecationsRoutes(reporting, logger); + registerDiagnosticRoutes(reporting, logger); registerJobGenerationRoutes(reporting, logger); registerJobInfoRoutes(reporting); - registerDiagnosticRoutes(reporting, logger); } export interface ReportingRequestPre { From 24ec3b78c581e076c208fd79cb8c9b68641d86cb Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 26 May 2021 11:36:13 +0200 Subject: [PATCH 09/16] remove TODO and added manual step --- .../migrate_existing_indices_ilm_policy.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts index 16bbfd50a0646..c463ba798e0c8 100644 --- a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts +++ b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts @@ -37,7 +37,6 @@ export const migrateExistingIndicesIlmPolicy = async ( message: i18n.translate('xpack.reporting.deprecations.migrateIndexIlmPolicyActionMessage', { defaultMessage: `All new reporting indices will be managed by a provisioned ILM policy: "${reportingIlmPolicy}". To manage the lifecycle of reports edit the ${reportingIlmPolicy} policy. Please note, this action will target all indices prefixed with "${indexPattern}".`, }), - documentationUrl: 'WIP', // TODO: Fix this! correctiveActions: { api: { method: 'PUT', @@ -50,6 +49,19 @@ export const migrateExistingIndicesIlmPolicy = async ( }, }, }, + manualSteps: [ + i18n.translate( + 'xpack.reporting.deprecations.migrateIndexIlmPolicy.stepOneDescription', + { + defaultMessage: + 'Send a request to Elasticsearch that configures indices matching "{indexPattern}" to be managed by the "{reportingIlmPolicy}" Index Lifecycle Policy.', + values: { + indexPattern, + reportingIlmPolicy, + }, + } + ), + ], }, }, ]; From bf030cb630b1391f4386a83315e3103bc6a7f64a Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 26 May 2021 11:37:48 +0200 Subject: [PATCH 10/16] removed unnecessary api request body --- .../deprecations/migrate_existing_indices_ilm_policy.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts index c463ba798e0c8..22f40939a2357 100644 --- a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts +++ b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts @@ -41,13 +41,6 @@ export const migrateExistingIndicesIlmPolicy = async ( api: { method: 'PUT', path: API_MIGRATE_ILM_POLICY_URL, - body: { - index: { - lifecycle: { - name: reportingIlmPolicy, - }, - }, - }, }, manualSteps: [ i18n.translate( From 7a0ff6d9ce80890117bdb327d7556b3c00413ac5 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 26 May 2021 12:06:09 +0200 Subject: [PATCH 11/16] added jest test for deprecations registration --- ...igrage_existing_indices_ilm_policy.test.ts | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 x-pack/plugins/reporting/server/deprecations/migrage_existing_indices_ilm_policy.test.ts diff --git a/x-pack/plugins/reporting/server/deprecations/migrage_existing_indices_ilm_policy.test.ts b/x-pack/plugins/reporting/server/deprecations/migrage_existing_indices_ilm_policy.test.ts new file mode 100644 index 0000000000000..8be146caa3094 --- /dev/null +++ b/x-pack/plugins/reporting/server/deprecations/migrage_existing_indices_ilm_policy.test.ts @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { GetDeprecationsContext } from 'src/core/server'; +import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; + +import { ReportingCore } from '../core'; +import { createMockConfigSchema, createMockReportingCore } from '../test_helpers'; + +import { migrateExistingIndicesIlmPolicy } from './migrate_existing_indices_ilm_policy'; + +type ScopedClusterClientMock = ReturnType< + typeof elasticsearchServiceMock.createScopedClusterClient +>; + +const { createApiResponse } = elasticsearchServiceMock; + +describe("Migrate existing indices' ILM policy", () => { + let esClient: ScopedClusterClientMock; + let deprecationsCtx: GetDeprecationsContext; + let reportingCore: ReportingCore; + + beforeEach(async () => { + esClient = elasticsearchServiceMock.createScopedClusterClient(); + deprecationsCtx = { esClient, savedObjectsClient: savedObjectsClientMock.create() }; + reportingCore = await createMockReportingCore(createMockConfigSchema()); + }); + + const createIndexSettings = (lifecycleName: string) => ({ + aliases: {}, + mappings: {}, + settings: { + index: { + lifecycle: { + name: lifecycleName, + }, + }, + }, + }); + + it('returns deprecation information when reporting indices are not using the reporting ILM policy', async () => { + esClient.asInternalUser.indices.getSettings.mockResolvedValueOnce( + createApiResponse({ + body: { + indexA: createIndexSettings('not-reporting-lifecycle'), + indexB: createIndexSettings('kibana-reporting'), + }, + }) + ); + + expect(await migrateExistingIndicesIlmPolicy(deprecationsCtx, { reportingCore })) + .toMatchInlineSnapshot(` + Array [ + Object { + "correctiveActions": Object { + "api": Object { + "method": "PUT", + "path": "/api/reporting/deprecations/migrate_ilm_policy", + }, + "manualSteps": Array [ + "Send a request to Elasticsearch that configures indices matching \\".reporting-*\\" to be managed by the \\"kibana-reporting\\" Index Lifecycle Policy.", + ], + }, + "level": "warning", + "message": "All new reporting indices will be managed by a provisioned ILM policy: \\"kibana-reporting\\". To manage the lifecycle of reports edit the kibana-reporting policy. Please note, this action will target all indices prefixed with \\".reporting-*\\".", + }, + ] + `); + }); + + it('does not return deprecations when all reporting indices are managed by the provisioned ILM policy', async () => { + esClient.asInternalUser.indices.getSettings.mockResolvedValueOnce( + createApiResponse({ + body: { + indexA: createIndexSettings('kibana-reporting'), + indexB: createIndexSettings('kibana-reporting'), + }, + }) + ); + + expect( + await migrateExistingIndicesIlmPolicy(deprecationsCtx, { reportingCore }) + ).toMatchInlineSnapshot(`Array []`); + + esClient.asInternalUser.indices.getSettings.mockResolvedValueOnce( + createApiResponse({ + body: {}, + }) + ); + + expect( + await migrateExistingIndicesIlmPolicy(deprecationsCtx, { reportingCore }) + ).toMatchInlineSnapshot(`Array []`); + }); +}); From 647827e2e97f47b33ee9816d728bb7d72f80ff30 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 26 May 2021 14:05:16 +0200 Subject: [PATCH 12/16] use the scoped elasticsearch user --- .../reporting/server/routes/deprecations.ts | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/reporting/server/routes/deprecations.ts b/x-pack/plugins/reporting/server/routes/deprecations.ts index 4bd4f4ad04d28..26172d3ea1737 100644 --- a/x-pack/plugins/reporting/server/routes/deprecations.ts +++ b/x-pack/plugins/reporting/server/routes/deprecations.ts @@ -12,39 +12,44 @@ import { LevelLogger as Logger } from '../lib'; export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Logger) => { const { router } = reporting.getPluginSetupDeps(); - router.put({ path: API_MIGRATE_ILM_POLICY_URL, validate: false }, async (ctx, req, res) => { - const store = await reporting.getStore(); - const { asInternalUser: client } = await reporting.getEsClient(); + router.put( + { path: API_MIGRATE_ILM_POLICY_URL, validate: false }, + async ({ core: { elasticsearch } }, req, res) => { + const store = await reporting.getStore(); + const { + client: { asCurrentUser: client }, + } = elasticsearch; - const indexPattern = store.getReportingIndexPattern(); - const reportingIlmPolicy = store.getIlmPolicyName(); + const indexPattern = store.getReportingIndexPattern(); + const reportingIlmPolicy = store.getIlmPolicyName(); - try { - await client.indices.putSettings({ - index: indexPattern, - body: { - index: { - lifecycle: { - name: reportingIlmPolicy, - }, - }, - }, - }); - return res.ok(); - } catch (err) { - logger.error(err); - - if (err instanceof errors.ResponseError) { - return res.customError({ - statusCode: 500, + try { + await client.indices.putSettings({ + index: indexPattern, body: { - message: err.message, - name: err.name, + index: { + lifecycle: { + name: reportingIlmPolicy, + }, + }, }, }); - } + return res.ok(); + } catch (err) { + logger.error(err); + + if (err instanceof errors.ResponseError) { + return res.customError({ + statusCode: 500, + body: { + message: err.message, + name: err.name, + }, + }); + } - throw err; + throw err; + } } - }); + ); }; From f9e66a3323201133a1414e2da67f0211b397b91b Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 26 May 2021 14:05:35 +0200 Subject: [PATCH 13/16] added jest test for deprecations route --- .../server/routes/deprecations.test.ts | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 x-pack/plugins/reporting/server/routes/deprecations.test.ts diff --git a/x-pack/plugins/reporting/server/routes/deprecations.test.ts b/x-pack/plugins/reporting/server/routes/deprecations.test.ts new file mode 100644 index 0000000000000..b6e0ba7181bb2 --- /dev/null +++ b/x-pack/plugins/reporting/server/routes/deprecations.test.ts @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import supertest from 'supertest'; +import { UnwrapPromise } from '@kbn/utility-types'; +import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; +import { of } from 'rxjs'; +import { ElasticsearchClient } from 'kibana/server'; +import { setupServer } from 'src/core/server/test_utils'; +import { elasticsearchServiceMock } from 'src/core/server/mocks'; + +import { API_MIGRATE_ILM_POLICY_URL } from '../../common/constants'; +import { ReportingCore } from '..'; +import { ReportingInternalSetup } from '../core'; +import { + createMockConfigSchema, + createMockPluginSetup, + createMockReportingCore, + createMockLevelLogger, +} from '../test_helpers'; +import { ReportingRequestHandlerContext } from '../types'; + +import { registerDeprecationsRoutes } from './deprecations'; + +type SetupServerReturn = UnwrapPromise>; +type MockLogger = ReturnType; + +const { createApiResponse } = elasticsearchServiceMock; + +describe(`PUT ${API_MIGRATE_ILM_POLICY_URL}`, () => { + const reportingSymbol = Symbol('reporting'); + let server: SetupServerReturn['server']; + let handlerContext: SetupServerReturn['handlerContext']; + let httpSetup: SetupServerReturn['httpSetup']; + let core: ReportingCore; + let mockSetupDeps: ReportingInternalSetup; + let logger: MockLogger; + let mockEsClient: DeeplyMockedKeys; + + beforeEach(async () => { + logger = createMockLevelLogger(); + ({ server, httpSetup, handlerContext } = await setupServer(reportingSymbol)); + + httpSetup.registerRouteHandlerContext( + reportingSymbol, + 'reporting', + () => ({ usesUiCapabilities: jest.fn() }) + ); + mockSetupDeps = createMockPluginSetup({ + security: { + license: { + isEnabled: () => true, + }, + authc: { + getCurrentUser: () => ({ + id: '123', + roles: ['superuser'], + username: 'Tom Riddle', + }), + }, + }, + router: httpSetup.createRouter(''), + licensing: { + license$: of({ + isActive: true, + isAvailable: true, + type: 'gold', + }), + }, + }); + + core = await createMockReportingCore( + createMockConfigSchema({ roles: { enabled: false } }), + mockSetupDeps + ); + + mockEsClient = handlerContext.elasticsearch.client.asCurrentUser; + }); + + afterEach(async () => { + await server.stop(); + }); + + it('sends the expected request to Elasticsearch', async () => { + mockEsClient.indices.putSettings.mockResolvedValueOnce(createApiResponse()); + registerDeprecationsRoutes(core, logger); + + await server.start(); + + await supertest(httpSetup.server.listener) + .put(API_MIGRATE_ILM_POLICY_URL) + .expect(200) + .then((response) => { + expect(response.body).toMatchInlineSnapshot(`Object {}`); // empty body + }); + + expect(mockEsClient.indices.putSettings).toHaveBeenCalledTimes(1); + expect(mockEsClient.indices.putSettings.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "body": Object { + "index": Object { + "lifecycle": Object { + "name": "kibana-reporting", + }, + }, + }, + "index": ".reporting-*", + } + `); + }); +}); From d3d3211961880b13f546c61bcf7307a019ce9bc5 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 26 May 2021 14:12:37 +0200 Subject: [PATCH 14/16] added additional test for error responses --- .../server/routes/deprecations.test.ts | 43 +++++++++++++++++++ .../reporting/server/routes/deprecations.ts | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/reporting/server/routes/deprecations.test.ts b/x-pack/plugins/reporting/server/routes/deprecations.test.ts index b6e0ba7181bb2..509d12cdf9de1 100644 --- a/x-pack/plugins/reporting/server/routes/deprecations.test.ts +++ b/x-pack/plugins/reporting/server/routes/deprecations.test.ts @@ -6,6 +6,7 @@ */ import supertest from 'supertest'; +import { errors } from '@elastic/elasticsearch'; import { UnwrapPromise } from '@kbn/utility-types'; import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; import { of } from 'rxjs'; @@ -112,4 +113,46 @@ describe(`PUT ${API_MIGRATE_ILM_POLICY_URL}`, () => { } `); }); + + it('returns the ES error in case something goes wrong', async () => { + mockEsClient.indices.putSettings.mockRejectedValueOnce( + new errors.ResponseError({ + body: { message: 'something is wrong' }, + headers: {}, + meta: {} as any, + statusCode: 503, + warnings: [], + }) + ); + registerDeprecationsRoutes(core, logger); + + await server.start(); + + await supertest(httpSetup.server.listener) + .put(API_MIGRATE_ILM_POLICY_URL) + .expect(503) + .then((response) => { + expect(response.body).toMatchInlineSnapshot(` + Object { + "error": "Service Unavailable", + "message": "Response Error", + "statusCode": 503, + } + `); + }); + + expect(mockEsClient.indices.putSettings).toHaveBeenCalledTimes(1); + expect(mockEsClient.indices.putSettings.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "body": Object { + "index": Object { + "lifecycle": Object { + "name": "kibana-reporting", + }, + }, + }, + "index": ".reporting-*", + } + `); + }); }); diff --git a/x-pack/plugins/reporting/server/routes/deprecations.ts b/x-pack/plugins/reporting/server/routes/deprecations.ts index 26172d3ea1737..7c5d04908d33d 100644 --- a/x-pack/plugins/reporting/server/routes/deprecations.ts +++ b/x-pack/plugins/reporting/server/routes/deprecations.ts @@ -40,7 +40,7 @@ export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Log if (err instanceof errors.ResponseError) { return res.customError({ - statusCode: 500, + statusCode: err.statusCode ?? 500, body: { message: err.message, name: err.name, From cb5ecdc31f8881b36e44cf35ce4a59e13f5d8c0b Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 26 May 2021 14:15:11 +0200 Subject: [PATCH 15/16] fix i18n --- .../deprecations/migrate_existing_indices_ilm_policy.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts index 22f40939a2357..029ee6e3e113e 100644 --- a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts +++ b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts @@ -35,7 +35,11 @@ export const migrateExistingIndicesIlmPolicy = async ( { level: 'warning', message: i18n.translate('xpack.reporting.deprecations.migrateIndexIlmPolicyActionMessage', { - defaultMessage: `All new reporting indices will be managed by a provisioned ILM policy: "${reportingIlmPolicy}". To manage the lifecycle of reports edit the ${reportingIlmPolicy} policy. Please note, this action will target all indices prefixed with "${indexPattern}".`, + defaultMessage: `All new reporting indices will be managed by a provisioned ILM policy: "{reportingIlmPolicy}". To manage the lifecycle of reports edit the {reportingIlmPolicy} policy. Please note, this action will target all indices prefixed with "{indexPattern}".`, + values: { + reportingIlmPolicy, + indexPattern, + }, }), correctiveActions: { api: { From 62989f7fc6d9b150ae55163e2787d63984038303 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 27 May 2021 16:12:16 +0200 Subject: [PATCH 16/16] - moved deprecations.ts to deprecations folder - renmoved manual steps section from the ilm migration deprecation - fixed Jest tests - fixed issues after merging master --- .../reporting/server/deprecations.test.ts | 107 ------------------ .../plugins/reporting/server/deprecations.ts | 52 --------- .../reporting/server/deprecations/index.ts | 12 +- ...igrage_existing_indices_ilm_policy.test.ts | 19 ++-- .../migrate_existing_indices_ilm_policy.ts | 15 +-- .../deprecations/reporting_role.test.ts | 107 ++++++++++++++++++ .../server/deprecations/reporting_role.ts | 48 ++++++++ x-pack/plugins/reporting/server/plugin.ts | 2 - 8 files changed, 172 insertions(+), 190 deletions(-) delete mode 100644 x-pack/plugins/reporting/server/deprecations.test.ts delete mode 100644 x-pack/plugins/reporting/server/deprecations.ts create mode 100644 x-pack/plugins/reporting/server/deprecations/reporting_role.test.ts create mode 100644 x-pack/plugins/reporting/server/deprecations/reporting_role.ts diff --git a/x-pack/plugins/reporting/server/deprecations.test.ts b/x-pack/plugins/reporting/server/deprecations.test.ts deleted file mode 100644 index cce4721b941a0..0000000000000 --- a/x-pack/plugins/reporting/server/deprecations.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ReportingCore } from '.'; -import { registerDeprecations } from './deprecations'; -import { createMockConfigSchema, createMockReportingCore } from './test_helpers'; -import { coreMock, elasticsearchServiceMock } from 'src/core/server/mocks'; -import { GetDeprecationsContext, IScopedClusterClient } from 'kibana/server'; - -let reportingCore: ReportingCore; -let context: GetDeprecationsContext; -let esClient: jest.Mocked; - -beforeEach(async () => { - const mockReportingConfig = createMockConfigSchema({ roles: { enabled: false } }); - reportingCore = await createMockReportingCore(mockReportingConfig); - esClient = elasticsearchServiceMock.createScopedClusterClient(); - esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({ - body: { xyz: { username: 'normal_user', roles: ['data_analyst'] } }, - }); - context = ({ esClient } as unknown) as GetDeprecationsContext; -}); - -test('logs no deprecations when setup has no issues', async () => { - const { getDeprecations } = await registerDeprecations(reportingCore, coreMock.createSetup()); - expect(await getDeprecations(context)).toMatchInlineSnapshot(`Array []`); -}); - -test('logs a plain message when only a reporting_user role issue is found', async () => { - esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({ - body: { reportron: { username: 'reportron', roles: ['kibana_admin', 'reporting_user'] } }, - }); - - const { getDeprecations } = await registerDeprecations(reportingCore, coreMock.createSetup()); - expect(await getDeprecations(context)).toMatchInlineSnapshot(` - Array [ - Object { - "correctiveActions": Object { - "manualSteps": Array [ - "Create one or more custom roles that provide Kibana application privileges to reporting features in **Management > Security > Roles**.", - "Assign the custom role(s) as desired, and remove the \\"reporting_user\\" role from the user(s).", - ], - }, - "documentationUrl": "https://www.elastic.co/guide/en/kibana/current/secure-reporting.html", - "level": "critical", - "message": "The deprecated \\"reporting_user\\" role has been found for 1 user(s): \\"reportron\\"", - }, - ] - `); -}); - -test('logs multiple entries when multiple reporting_user role issues are found', async () => { - esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({ - body: { - reportron: { username: 'reportron', roles: ['kibana_admin', 'reporting_user'] }, - supercooluser: { username: 'supercooluser', roles: ['kibana_admin', 'reporting_user'] }, - }, - }); - - const { getDeprecations } = await registerDeprecations(reportingCore, coreMock.createSetup()); - expect(await getDeprecations(context)).toMatchInlineSnapshot(` - Array [ - Object { - "correctiveActions": Object { - "manualSteps": Array [ - "Create one or more custom roles that provide Kibana application privileges to reporting features in **Management > Security > Roles**.", - "Assign the custom role(s) as desired, and remove the \\"reporting_user\\" role from the user(s).", - ], - }, - "documentationUrl": "https://www.elastic.co/guide/en/kibana/current/secure-reporting.html", - "level": "critical", - "message": "The deprecated \\"reporting_user\\" role has been found for 2 user(s): \\"reportron\\", \\"supercooluser\\"", - }, - ] - `); -}); - -test('logs an expanded message when a config issue and a reporting_user role issue is found', async () => { - esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({ - body: { reportron: { username: 'reportron', roles: ['kibana_admin', 'reporting_user'] } }, - }); - - const mockReportingConfig = createMockConfigSchema({ roles: { enabled: true } }); - reportingCore = await createMockReportingCore(mockReportingConfig); - - const { getDeprecations } = await registerDeprecations(reportingCore, coreMock.createSetup()); - expect(await getDeprecations(context)).toMatchInlineSnapshot(` - Array [ - Object { - "correctiveActions": Object { - "manualSteps": Array [ - "Set \\"xpack.reporting.roles.enabled: false\\" in kibana.yml", - "Create one or more custom roles that provide Kibana application privileges to reporting features in **Management > Security > Roles**.", - "Assign the custom role(s) as desired, and remove the \\"reporting_user\\" role from the user(s).", - ], - }, - "documentationUrl": "https://www.elastic.co/guide/en/kibana/current/secure-reporting.html", - "level": "critical", - "message": "The deprecated \\"reporting_user\\" role has been found for 1 user(s): \\"reportron\\"", - }, - ] - `); -}); diff --git a/x-pack/plugins/reporting/server/deprecations.ts b/x-pack/plugins/reporting/server/deprecations.ts deleted file mode 100644 index 61074fff012a2..0000000000000 --- a/x-pack/plugins/reporting/server/deprecations.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { CoreSetup, DeprecationsDetails, RegisterDeprecationsConfig } from 'src/core/server'; -import { ReportingCore } from '.'; - -const deprecatedRole = 'reporting_user'; -const upgradableConfig = 'xpack.reporting.roles.enabled: false'; - -export async function registerDeprecations( - reporting: ReportingCore, - { deprecations: deprecationsService }: CoreSetup -) { - const deprecationsConfig: RegisterDeprecationsConfig = { - getDeprecations: async ({ esClient }) => { - const usingDeprecatedConfig = !reporting.getContract().usesUiCapabilities(); - const deprecations: DeprecationsDetails[] = []; - const { body: users } = await esClient.asCurrentUser.security.getUser(); - - const reportingUsers = Object.entries(users) - .filter(([username, user]) => user.roles.includes(deprecatedRole)) - .map(([, user]) => user.username); - const numReportingUsers = reportingUsers.length; - - if (numReportingUsers > 0) { - const usernames = reportingUsers.join('", "'); - deprecations.push({ - message: `The deprecated "${deprecatedRole}" role has been found for ${numReportingUsers} user(s): "${usernames}"`, - documentationUrl: 'https://www.elastic.co/guide/en/kibana/current/secure-reporting.html', - level: 'critical', - correctiveActions: { - manualSteps: [ - ...(usingDeprecatedConfig ? [`Set "${upgradableConfig}" in kibana.yml`] : []), - `Create one or more custom roles that provide Kibana application privileges to reporting features in **Management > Security > Roles**.`, - `Assign the custom role(s) as desired, and remove the "${deprecatedRole}" role from the user(s).`, - ], - }, - }); - } - - return deprecations; - }, - }; - - deprecationsService.registerDeprecations(deprecationsConfig); - - return deprecationsConfig; -} diff --git a/x-pack/plugins/reporting/server/deprecations/index.ts b/x-pack/plugins/reporting/server/deprecations/index.ts index 03d900b1b2e28..9ecb3b7ab88ad 100644 --- a/x-pack/plugins/reporting/server/deprecations/index.ts +++ b/x-pack/plugins/reporting/server/deprecations/index.ts @@ -7,7 +7,8 @@ import { CoreSetup } from 'src/core/server'; import { ReportingCore } from '../core'; -import { migrateExistingIndicesIlmPolicy } from './migrate_existing_indices_ilm_policy'; +import { getDeprecationsInfo as getIlmPolicyDeprecationsInfo } from './migrate_existing_indices_ilm_policy'; +import { getDeprecationsInfo as getReportingRoleDeprecationsInfo } from './reporting_role'; export const registerDeprecations = ({ core, @@ -17,10 +18,11 @@ export const registerDeprecations = ({ reportingCore: ReportingCore; }) => { core.deprecations.registerDeprecations({ - getDeprecations(ctx) { - return migrateExistingIndicesIlmPolicy(ctx, { - reportingCore, - }); + getDeprecations: async (ctx) => { + return [ + ...(await getIlmPolicyDeprecationsInfo(ctx, { reportingCore })), + ...(await getReportingRoleDeprecationsInfo(ctx, { reportingCore })), + ]; }, }); }; diff --git a/x-pack/plugins/reporting/server/deprecations/migrage_existing_indices_ilm_policy.test.ts b/x-pack/plugins/reporting/server/deprecations/migrage_existing_indices_ilm_policy.test.ts index 8be146caa3094..638373899943e 100644 --- a/x-pack/plugins/reporting/server/deprecations/migrage_existing_indices_ilm_policy.test.ts +++ b/x-pack/plugins/reporting/server/deprecations/migrage_existing_indices_ilm_policy.test.ts @@ -11,7 +11,7 @@ import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/serve import { ReportingCore } from '../core'; import { createMockConfigSchema, createMockReportingCore } from '../test_helpers'; -import { migrateExistingIndicesIlmPolicy } from './migrate_existing_indices_ilm_policy'; +import { getDeprecationsInfo } from './migrate_existing_indices_ilm_policy'; type ScopedClusterClientMock = ReturnType< typeof elasticsearchServiceMock.createScopedClusterClient @@ -19,7 +19,7 @@ type ScopedClusterClientMock = ReturnType< const { createApiResponse } = elasticsearchServiceMock; -describe("Migrate existing indices' ILM policy", () => { +describe("Migrate existing indices' ILM policy deprecations", () => { let esClient: ScopedClusterClientMock; let deprecationsCtx: GetDeprecationsContext; let reportingCore: ReportingCore; @@ -52,8 +52,7 @@ describe("Migrate existing indices' ILM policy", () => { }) ); - expect(await migrateExistingIndicesIlmPolicy(deprecationsCtx, { reportingCore })) - .toMatchInlineSnapshot(` + expect(await getDeprecationsInfo(deprecationsCtx, { reportingCore })).toMatchInlineSnapshot(` Array [ Object { "correctiveActions": Object { @@ -82,9 +81,9 @@ describe("Migrate existing indices' ILM policy", () => { }) ); - expect( - await migrateExistingIndicesIlmPolicy(deprecationsCtx, { reportingCore }) - ).toMatchInlineSnapshot(`Array []`); + expect(await getDeprecationsInfo(deprecationsCtx, { reportingCore })).toMatchInlineSnapshot( + `Array []` + ); esClient.asInternalUser.indices.getSettings.mockResolvedValueOnce( createApiResponse({ @@ -92,8 +91,8 @@ describe("Migrate existing indices' ILM policy", () => { }) ); - expect( - await migrateExistingIndicesIlmPolicy(deprecationsCtx, { reportingCore }) - ).toMatchInlineSnapshot(`Array []`); + expect(await getDeprecationsInfo(deprecationsCtx, { reportingCore })).toMatchInlineSnapshot( + `Array []` + ); }); }); diff --git a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts index 029ee6e3e113e..4facfc051e8be 100644 --- a/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts +++ b/x-pack/plugins/reporting/server/deprecations/migrate_existing_indices_ilm_policy.ts @@ -14,7 +14,7 @@ interface ExtraDependencies { reportingCore: ReportingCore; } -export const migrateExistingIndicesIlmPolicy = async ( +export const getDeprecationsInfo = async ( { esClient }: GetDeprecationsContext, { reportingCore }: ExtraDependencies ): Promise => { @@ -46,19 +46,6 @@ export const migrateExistingIndicesIlmPolicy = async ( method: 'PUT', path: API_MIGRATE_ILM_POLICY_URL, }, - manualSteps: [ - i18n.translate( - 'xpack.reporting.deprecations.migrateIndexIlmPolicy.stepOneDescription', - { - defaultMessage: - 'Send a request to Elasticsearch that configures indices matching "{indexPattern}" to be managed by the "{reportingIlmPolicy}" Index Lifecycle Policy.', - values: { - indexPattern, - reportingIlmPolicy, - }, - } - ), - ], }, }, ]; diff --git a/x-pack/plugins/reporting/server/deprecations/reporting_role.test.ts b/x-pack/plugins/reporting/server/deprecations/reporting_role.test.ts new file mode 100644 index 0000000000000..f8d294c937781 --- /dev/null +++ b/x-pack/plugins/reporting/server/deprecations/reporting_role.test.ts @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { GetDeprecationsContext, IScopedClusterClient } from 'kibana/server'; +import { elasticsearchServiceMock } from 'src/core/server/mocks'; + +import { ReportingCore } from '../'; +import { createMockConfigSchema, createMockReportingCore } from '../test_helpers'; + +import { getDeprecationsInfo } from './reporting_role'; + +describe('Reporting role deprecations', () => { + let reportingCore: ReportingCore; + let context: GetDeprecationsContext; + let esClient: jest.Mocked; + + beforeEach(async () => { + const mockReportingConfig = createMockConfigSchema({ roles: { enabled: false } }); + reportingCore = await createMockReportingCore(mockReportingConfig); + esClient = elasticsearchServiceMock.createScopedClusterClient(); + esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({ + body: { xyz: { username: 'normal_user', roles: ['data_analyst'] } }, + }); + context = ({ esClient } as unknown) as GetDeprecationsContext; + }); + + test('logs no deprecations when setup has no issues', async () => { + expect(await getDeprecationsInfo(context, { reportingCore })).toMatchInlineSnapshot(`Array []`); + }); + + test('logs a plain message when only a reporting_user role issue is found', async () => { + esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({ + body: { reportron: { username: 'reportron', roles: ['kibana_admin', 'reporting_user'] } }, + }); + + expect(await getDeprecationsInfo(context, { reportingCore })).toMatchInlineSnapshot(` + Array [ + Object { + "correctiveActions": Object { + "manualSteps": Array [ + "Create one or more custom roles that provide Kibana application privileges to reporting features in **Management > Security > Roles**.", + "Assign the custom role(s) as desired, and remove the \\"reporting_user\\" role from the user(s).", + ], + }, + "documentationUrl": "https://www.elastic.co/guide/en/kibana/current/secure-reporting.html", + "level": "critical", + "message": "The deprecated \\"reporting_user\\" role has been found for 1 user(s): \\"reportron\\"", + }, + ] + `); + }); + + test('logs multiple entries when multiple reporting_user role issues are found', async () => { + esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({ + body: { + reportron: { username: 'reportron', roles: ['kibana_admin', 'reporting_user'] }, + supercooluser: { username: 'supercooluser', roles: ['kibana_admin', 'reporting_user'] }, + }, + }); + + expect(await getDeprecationsInfo(context, { reportingCore })).toMatchInlineSnapshot(` + Array [ + Object { + "correctiveActions": Object { + "manualSteps": Array [ + "Create one or more custom roles that provide Kibana application privileges to reporting features in **Management > Security > Roles**.", + "Assign the custom role(s) as desired, and remove the \\"reporting_user\\" role from the user(s).", + ], + }, + "documentationUrl": "https://www.elastic.co/guide/en/kibana/current/secure-reporting.html", + "level": "critical", + "message": "The deprecated \\"reporting_user\\" role has been found for 2 user(s): \\"reportron\\", \\"supercooluser\\"", + }, + ] + `); + }); + + test('logs an expanded message when a config issue and a reporting_user role issue is found', async () => { + esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({ + body: { reportron: { username: 'reportron', roles: ['kibana_admin', 'reporting_user'] } }, + }); + + const mockReportingConfig = createMockConfigSchema({ roles: { enabled: true } }); + reportingCore = await createMockReportingCore(mockReportingConfig); + + expect(await getDeprecationsInfo(context, { reportingCore })).toMatchInlineSnapshot(` + Array [ + Object { + "correctiveActions": Object { + "manualSteps": Array [ + "Set \\"xpack.reporting.roles.enabled: false\\" in kibana.yml", + "Create one or more custom roles that provide Kibana application privileges to reporting features in **Management > Security > Roles**.", + "Assign the custom role(s) as desired, and remove the \\"reporting_user\\" role from the user(s).", + ], + }, + "documentationUrl": "https://www.elastic.co/guide/en/kibana/current/secure-reporting.html", + "level": "critical", + "message": "The deprecated \\"reporting_user\\" role has been found for 1 user(s): \\"reportron\\"", + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/reporting/server/deprecations/reporting_role.ts b/x-pack/plugins/reporting/server/deprecations/reporting_role.ts new file mode 100644 index 0000000000000..3ac6f02d91db2 --- /dev/null +++ b/x-pack/plugins/reporting/server/deprecations/reporting_role.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DeprecationsDetails, GetDeprecationsContext } from 'src/core/server'; +import { ReportingCore } from '../'; + +const deprecatedRole = 'reporting_user'; +const upgradableConfig = 'xpack.reporting.roles.enabled: false'; + +interface ExtraDependencies { + reportingCore: ReportingCore; +} + +export const getDeprecationsInfo = async ( + { esClient }: GetDeprecationsContext, + { reportingCore }: ExtraDependencies +): Promise => { + const usingDeprecatedConfig = !reportingCore.getContract().usesUiCapabilities(); + const deprecations: DeprecationsDetails[] = []; + const { body: users } = await esClient.asCurrentUser.security.getUser(); + + const reportingUsers = Object.entries(users) + .filter(([username, user]) => user.roles.includes(deprecatedRole)) + .map(([, user]) => user.username); + const numReportingUsers = reportingUsers.length; + + if (numReportingUsers > 0) { + const usernames = reportingUsers.join('", "'); + deprecations.push({ + message: `The deprecated "${deprecatedRole}" role has been found for ${numReportingUsers} user(s): "${usernames}"`, + documentationUrl: 'https://www.elastic.co/guide/en/kibana/current/secure-reporting.html', + level: 'critical', + correctiveActions: { + manualSteps: [ + ...(usingDeprecatedConfig ? [`Set "${upgradableConfig}" in kibana.yml`] : []), + `Create one or more custom roles that provide Kibana application privileges to reporting features in **Management > Security > Roles**.`, + `Assign the custom role(s) as desired, and remove the "${deprecatedRole}" role from the user(s).`, + ], + }, + }); + } + + return deprecations; +}; diff --git a/x-pack/plugins/reporting/server/plugin.ts b/x-pack/plugins/reporting/server/plugin.ts index f3736b24bfa81..a5f722306f40f 100644 --- a/x-pack/plugins/reporting/server/plugin.ts +++ b/x-pack/plugins/reporting/server/plugin.ts @@ -22,7 +22,6 @@ import type { ReportingStartDeps, } from './types'; import { registerReportingUsageCollector } from './usage'; -import { registerDeprecations } from './deprecations'; export class ReportingPlugin implements Plugin { @@ -66,7 +65,6 @@ export class ReportingPlugin }); registerUiSettings(core); - registerDeprecations(reportingCore, core); registerReportingUsageCollector(reportingCore, plugins); registerRoutes(reportingCore, this.logger); registerDeprecations({