From fa5fe8b6999001038c6b7a7cc83d5c8794e130c7 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Thu, 9 Apr 2020 17:09:23 +0200 Subject: [PATCH] register logstash route handlers in NP --- x-pack/legacy/plugins/logstash/index.js | 8 --- x-pack/plugins/logstash/kibana.json | 1 + .../lib/check_license/check_license.test.ts | 59 +++++++++++++++++ .../server/lib/check_license/check_license.ts | 65 +++++++++++++++++++ .../server/lib/check_license/index.ts | 7 ++ .../fetch_all_from_scroll.ts | 4 +- .../server/models/pipeline/pipeline.ts | 9 +-- .../pipeline_list_item.test.ts | 3 + .../pipeline_list_item/pipeline_list_item.ts | 17 ++--- x-pack/plugins/logstash/server/plugin.ts | 21 +++++- x-pack/plugins/logstash/server/types.ts | 18 +++++ 11 files changed, 184 insertions(+), 28 deletions(-) create mode 100755 x-pack/plugins/logstash/server/lib/check_license/check_license.test.ts create mode 100644 x-pack/plugins/logstash/server/lib/check_license/check_license.ts create mode 100644 x-pack/plugins/logstash/server/lib/check_license/index.ts create mode 100644 x-pack/plugins/logstash/server/types.ts diff --git a/x-pack/legacy/plugins/logstash/index.js b/x-pack/legacy/plugins/logstash/index.js index fcabcd78705b1e..29f01032f34131 100755 --- a/x-pack/legacy/plugins/logstash/index.js +++ b/x-pack/legacy/plugins/logstash/index.js @@ -5,10 +5,6 @@ */ import { resolve } from 'path'; -import { registerLogstashPipelinesRoutes } from './server/routes/api/pipelines'; -import { registerLogstashPipelineRoutes } from './server/routes/api/pipeline'; -import { registerLogstashUpgradeRoutes } from './server/routes/api/upgrade'; -import { registerLogstashClusterRoutes } from './server/routes/api/cluster'; import { registerLicenseChecker } from './server/lib/register_license_checker'; import { PLUGIN } from '../../../plugins/logstash/common/constants'; @@ -32,9 +28,5 @@ export const logstash = kibana => }, init: server => { registerLicenseChecker(server); - registerLogstashPipelinesRoutes(server); - registerLogstashPipelineRoutes(server); - registerLogstashUpgradeRoutes(server); - registerLogstashClusterRoutes(server); }, }); diff --git a/x-pack/plugins/logstash/kibana.json b/x-pack/plugins/logstash/kibana.json index 54fc57625f53bb..bcc926535d3c28 100644 --- a/x-pack/plugins/logstash/kibana.json +++ b/x-pack/plugins/logstash/kibana.json @@ -6,6 +6,7 @@ "requiredPlugins": [ "licensing" ], + "optionalPlugins": ["security"], "server": true, "ui": false } diff --git a/x-pack/plugins/logstash/server/lib/check_license/check_license.test.ts b/x-pack/plugins/logstash/server/lib/check_license/check_license.test.ts new file mode 100755 index 00000000000000..6e88b485c471d1 --- /dev/null +++ b/x-pack/plugins/logstash/server/lib/check_license/check_license.test.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { licensingMock } from '../../../../licensing/server/mocks'; +import { checkLicense } from './check_license'; + +describe('check_license', function() { + describe('returns "valid": false & message', () => { + it('license information is not available', () => { + const license = licensingMock.createLicenseMock(); + license.isAvailable = false; + + const { valid, message } = checkLicense(license); + + expect(valid).toBe(false); + expect(message).toStrictEqual(expect.any(String)); + }); + + it('license level is not enough', () => { + const license = licensingMock.createLicenseMock(); + license.hasAtLeast.mockReturnValue(false); + + const { valid, message } = checkLicense(license); + + expect(valid).toBe(false); + expect(message).toStrictEqual(expect.any(String)); + }); + + it('license is expired', () => { + const license = licensingMock.createLicenseMock(); + license.isActive = false; + + const { valid, message } = checkLicense(license); + + expect(valid).toBe(false); + expect(message).toStrictEqual(expect.any(String)); + }); + + it('elasticsearch security is disabled', () => { + const license = licensingMock.createLicenseMock(); + license.getFeature.mockReturnValue({ isEnabled: false, isAvailable: false }); + + const { valid, message } = checkLicense(license); + + expect(valid).toBe(false); + expect(message).toStrictEqual(expect.any(String)); + }); + }); + it('returns "valid": true without message otherwise', () => { + const license = licensingMock.createLicenseMock(); + + const { valid, message } = checkLicense(license); + + expect(valid).toBe(true); + expect(message).toBe(null); + }); +}); diff --git a/x-pack/plugins/logstash/server/lib/check_license/check_license.ts b/x-pack/plugins/logstash/server/lib/check_license/check_license.ts new file mode 100644 index 00000000000000..4eef2eb9b06811 --- /dev/null +++ b/x-pack/plugins/logstash/server/lib/check_license/check_license.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +import { CheckLicense } from '../../../../licensing/server'; + +export const checkLicense: CheckLicense = license => { + if (!license.isAvailable) { + return { + valid: false, + message: i18n.translate( + 'xpack.logstash.managementSection.notPossibleToManagePipelinesMessage', + { + defaultMessage: + 'You cannot manage Logstash pipelines because license information is not available at this time.', + } + ), + }; + } + + if (!license.hasAtLeast('standard')) { + return { + valid: false, + message: i18n.translate('xpack.logstash.managementSection.licenseDoesNotSupportDescription', { + defaultMessage: + 'Your {licenseType} license does not support Logstash pipeline management features. Please upgrade your license.', + values: { licenseType: license.type }, + }), + }; + } + + if (!license.isActive) { + return { + valid: false, + message: i18n.translate( + 'xpack.logstash.managementSection.pipelineCrudOperationsNotAllowedDescription', + { + defaultMessage: + 'You cannot edit, create, or delete your Logstash pipelines because your {licenseType} license has expired.', + values: { licenseType: license.type }, + } + ), + }; + } + + if (!license.getFeature('security').isEnabled) { + return { + valid: false, + message: i18n.translate('xpack.logstash.managementSection.enableSecurityDescription', { + defaultMessage: + 'Security must be enabled in order to use Logstash pipeline management features.' + + ' Please set xpack.security.enabled: true in your elasticsearch.yml.', + }), + }; + } + + return { + valid: true, + message: null, + }; +}; diff --git a/x-pack/plugins/logstash/server/lib/check_license/index.ts b/x-pack/plugins/logstash/server/lib/check_license/index.ts new file mode 100644 index 00000000000000..f2c070fd44b6e6 --- /dev/null +++ b/x-pack/plugins/logstash/server/lib/check_license/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { checkLicense } from './check_license'; diff --git a/x-pack/plugins/logstash/server/lib/fetch_all_from_scroll/fetch_all_from_scroll.ts b/x-pack/plugins/logstash/server/lib/fetch_all_from_scroll/fetch_all_from_scroll.ts index 57411d79007190..060cf188a4c60e 100755 --- a/x-pack/plugins/logstash/server/lib/fetch_all_from_scroll/fetch_all_from_scroll.ts +++ b/x-pack/plugins/logstash/server/lib/fetch_all_from_scroll/fetch_all_from_scroll.ts @@ -7,14 +7,14 @@ import { APICaller } from 'src/core/server'; import { SearchResponse } from 'elasticsearch'; import { ES_SCROLL_SETTINGS } from '../../../common/constants'; -type Hits = SearchResponse['hits']['hits']; +import { Hits } from '../../types'; export async function fetchAllFromScroll( response: SearchResponse, callWithRequest: APICaller, hits: Hits = [] ): Promise { - const newHits = response.hits.hits; + const newHits = response.hits?.hits || []; const scrollId = response._scroll_id; if (newHits.length > 0) { diff --git a/x-pack/plugins/logstash/server/models/pipeline/pipeline.ts b/x-pack/plugins/logstash/server/models/pipeline/pipeline.ts index f28af33597e64e..3f2debeebeb46b 100755 --- a/x-pack/plugins/logstash/server/models/pipeline/pipeline.ts +++ b/x-pack/plugins/logstash/server/models/pipeline/pipeline.ts @@ -12,15 +12,15 @@ import { i18n } from '@kbn/i18n'; interface PipelineOptions { id: string; description: string; - username: string; pipeline: string; + username?: string; settings?: Record; } interface DownstreamPipeline { description: string; pipeline: string; - settings: Record; + settings?: Record; } /** * This model deals with a pipeline object from ES and converts it to Kibana downstream @@ -28,9 +28,10 @@ interface DownstreamPipeline { export class Pipeline { public readonly id: string; public readonly description: string; - public readonly username: string; + public readonly username?: string; public readonly pipeline: string; private readonly settings: Record; + constructor(options: PipelineOptions) { this.id = options.id; this.description = options.description; @@ -77,7 +78,7 @@ export class Pipeline { static fromDownstreamJSON( downstreamPipeline: DownstreamPipeline, pipelineId: string, - username: string + username?: string ) { const opts = { id: pipelineId, diff --git a/x-pack/plugins/logstash/server/models/pipeline_list_item/pipeline_list_item.test.ts b/x-pack/plugins/logstash/server/models/pipeline_list_item/pipeline_list_item.test.ts index f909e90ce43caf..c557e84443b02a 100755 --- a/x-pack/plugins/logstash/server/models/pipeline_list_item/pipeline_list_item.test.ts +++ b/x-pack/plugins/logstash/server/models/pipeline_list_item/pipeline_list_item.test.ts @@ -20,6 +20,9 @@ describe('pipeline_list_item', () => { username: 'elastic', pipeline: 'input {} filter { grok {} }\n output {}', }, + _index: 'index', + _type: 'type', + _score: 100, }; describe('fromUpstreamJSON factory method', () => { diff --git a/x-pack/plugins/logstash/server/models/pipeline_list_item/pipeline_list_item.ts b/x-pack/plugins/logstash/server/models/pipeline_list_item/pipeline_list_item.ts index 68f91934adf432..98c91fca1fcca7 100755 --- a/x-pack/plugins/logstash/server/models/pipeline_list_item/pipeline_list_item.ts +++ b/x-pack/plugins/logstash/server/models/pipeline_list_item/pipeline_list_item.ts @@ -5,18 +5,13 @@ */ import { get } from 'lodash'; +import { Hit, PipelineListItemOptions } from '../../types'; -interface PipelineListItemOptions { - id: string; - description: string; - last_modified: string; - username: string; -} export class PipelineListItem { - private readonly id: string; - private readonly description: string; - private readonly last_modified: string; - private readonly username: string; + public readonly id: string; + public readonly description: string; + public readonly last_modified: string; + public readonly username: string; constructor(options: PipelineListItemOptions) { this.id = options.id; this.description = options.description; @@ -39,7 +34,7 @@ export class PipelineListItem { * Takes the json GET response from ES and constructs a pipeline model to be used * in Kibana downstream */ - static fromUpstreamJSON(pipeline: Record) { + static fromUpstreamJSON(pipeline: Hit) { const opts = { id: pipeline._id, description: get(pipeline, '_source.description'), diff --git a/x-pack/plugins/logstash/server/plugin.ts b/x-pack/plugins/logstash/server/plugin.ts index 903d885c4ecd91..be039427505e9c 100644 --- a/x-pack/plugins/logstash/server/plugin.ts +++ b/x-pack/plugins/logstash/server/plugin.ts @@ -3,26 +3,41 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { CoreSetup, Logger, Plugin, PluginInitializerContext } from 'src/core/server'; +import { + CoreSetup, + ICustomClusterClient, + Logger, + Plugin, + PluginInitializerContext, +} from 'src/core/server'; import { LicensingPluginSetup } from '../../licensing/server'; +import { SecurityPluginSetup } from '../../security/server'; import { registerRoutes } from './routes'; interface SetupDeps { licensing: LicensingPluginSetup; + security?: SecurityPluginSetup; } export class LogstashPlugin implements Plugin { private readonly logger: Logger; + private esClient?: ICustomClusterClient; constructor(context: PluginInitializerContext) { this.logger = context.logger.get(); } setup(core: CoreSetup, deps: SetupDeps) { this.logger.debug('Setting up Logstash plugin'); - registerRoutes(core.http.createRouter()); + const esClient = core.elasticsearch.createClient('logstash'); + + registerRoutes(core.http.createRouter(), esClient, deps.security); } start() {} - stop() {} + stop() { + if (this.esClient) { + this.esClient.close(); + } + } } diff --git a/x-pack/plugins/logstash/server/types.ts b/x-pack/plugins/logstash/server/types.ts new file mode 100644 index 00000000000000..63647a07826d30 --- /dev/null +++ b/x-pack/plugins/logstash/server/types.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { SearchResponse } from 'elasticsearch'; + +type UnwrapArray = T extends Array ? U : T; + +export type Hits = SearchResponse['hits']['hits']; +export type Hit = UnwrapArray; + +export interface PipelineListItemOptions { + id: string; + description: string; + last_modified: string; + username: string; +}