From 94127d803a1fe3b2a5aca9a5ded9e35ad2351086 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Wed, 6 May 2020 09:57:26 +0100 Subject: [PATCH] [Alerting] migrates acceptance and functional test fixtures to KP (#64888) This PR migrates the vast majority of Alerting legacy code to the Kibana Platform. This includes: 1. Removed legacy Task Manager 2. Migrates Fixture plugins in Alerting, Triggers UI and Task Manager Perf This does not includes: 1. The PagerDuty simulator due to a lack of support for custom responses in the platform. issue opened. https://github.com/elastic/kibana/issues/65045 2. The Webhooks simulator due to a lack of support for custom authorisation. Requires investigation. --- renovate.json5 | 16 + test/scripts/jenkins_xpack_build_kibana.sh | 2 + x-pack/index.js | 2 - .../plugins/task_manager/server/index.ts | 66 --- .../plugins/task_manager/server/legacy.ts | 58 -- x-pack/package.json | 2 + x-pack/plugins/actions/server/plugin.ts | 14 +- x-pack/plugins/alerting/server/plugin.ts | 14 +- .../apm/server/lib/apm_telemetry/index.ts | 30 +- .../server/create_task_manager.test.ts | 62 --- .../server/create_task_manager.ts | 43 -- x-pack/plugins/task_manager/server/mocks.ts | 1 - x-pack/plugins/task_manager/server/plugin.ts | 62 ++- .../task_manager/server/task_manager.mock.ts | 1 - .../builtin_action_types/servicenow.ts | 2 +- .../actions/builtin_action_types/slack.ts | 2 +- .../actions/builtin_action_types/webhook.ts | 2 +- .../alerting_api_integration/common/config.ts | 16 +- .../common/fixtures/plugins/aad/index.ts | 59 -- .../common/fixtures/plugins/aad/kibana.json | 10 + .../common/fixtures/plugins/aad/package.json | 17 +- .../fixtures/plugins/aad/server}/index.ts | 4 +- .../fixtures/plugins/aad/server/plugin.ts | 64 +++ .../plugins/actions_simulators/index.ts | 91 --- .../actions_simulators/jira_simulation.ts | 101 ---- .../plugins/actions_simulators/kibana.json | 9 + .../plugins/actions_simulators/package.json | 19 +- .../pagerduty_simulation.ts | 79 --- .../actions_simulators/{ => server}/README.md | 0 .../actions_simulators/server/index.ts | 9 + .../server/jira_simulation.ts | 116 ++++ .../server/pagerduty_simulation.ts | 86 +++ .../actions_simulators/server/plugin.ts | 95 ++++ .../server/servicenow_simulation.ts | 99 ++++ .../servicenow_simulation.ts | 81 --- .../actions_simulators_legacy/index.ts | 26 + .../actions_simulators_legacy/package.json | 7 + .../slack_simulation.ts | 0 .../webhook_simulation.ts | 0 .../common/fixtures/plugins/alerts/index.ts | 501 ----------------- .../fixtures/plugins/alerts/kibana.json | 10 + .../fixtures/plugins/alerts/package.json | 19 +- .../fixtures/plugins/alerts/server/index.ts | 9 + .../fixtures/plugins/alerts/server/plugin.ts | 516 ++++++++++++++++++ .../fixtures/plugins/task_manager/index.ts | 60 -- .../plugins/task_manager/package.json | 12 - .../plugins/task_manager_fixture/kibana.json | 9 + .../plugins/task_manager_fixture/package.json | 20 + .../task_manager_fixture/server/index.ts | 9 + .../task_manager_fixture/server/plugin.ts | 87 +++ .../actions/builtin_action_types/jira.ts | 2 +- .../actions/builtin_action_types/pagerduty.ts | 2 +- .../builtin_action_types/servicenow.ts | 2 +- .../actions/builtin_action_types/slack.ts | 2 +- .../actions/builtin_action_types/webhook.ts | 2 +- .../actions/builtin_action_types/webhook.ts | 2 +- x-pack/test/plugin_api_perf/config.js | 2 +- .../plugins/task_manager_performance/index.js | 378 ------------- .../task_manager_performance/init_routes.js | 77 --- .../task_manager_performance/kibana.json | 9 + .../task_manager_performance/package.json | 15 +- .../task_manager_performance/server/index.ts | 9 + .../server/init_routes.ts | 92 ++++ .../task_manager_performance/server/plugin.ts | 404 ++++++++++++++ .../task_manager_performance/server/types.ts | 82 +++ x-pack/typings/hapi.d.ts | 4 +- yarn.lock | 21 +- 67 files changed, 1964 insertions(+), 1760 deletions(-) delete mode 100644 x-pack/legacy/plugins/task_manager/server/index.ts delete mode 100644 x-pack/legacy/plugins/task_manager/server/legacy.ts delete mode 100644 x-pack/plugins/task_manager/server/create_task_manager.test.ts delete mode 100644 x-pack/plugins/task_manager/server/create_task_manager.ts delete mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/index.ts create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/kibana.json rename x-pack/{legacy/plugins/task_manager => test/alerting_api_integration/common/fixtures/plugins/aad/server}/index.ts (72%) create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/server/plugin.ts delete mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/index.ts delete mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/jira_simulation.ts create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/kibana.json delete mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/pagerduty_simulation.ts rename x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/{ => server}/README.md (100%) create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/index.ts create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/jira_simulation.ts create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/pagerduty_simulation.ts create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin.ts create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/servicenow_simulation.ts delete mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/servicenow_simulation.ts create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators_legacy/index.ts create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators_legacy/package.json rename x-pack/test/alerting_api_integration/common/fixtures/plugins/{actions_simulators => actions_simulators_legacy}/slack_simulation.ts (100%) rename x-pack/test/alerting_api_integration/common/fixtures/plugins/{actions_simulators => actions_simulators_legacy}/webhook_simulation.ts (100%) delete mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/index.ts create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts delete mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager/index.ts delete mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager/package.json create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/kibana.json create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/package.json create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/server/index.ts create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/server/plugin.ts delete mode 100644 x-pack/test/plugin_api_perf/plugins/task_manager_performance/index.js delete mode 100644 x-pack/test/plugin_api_perf/plugins/task_manager_performance/init_routes.js create mode 100644 x-pack/test/plugin_api_perf/plugins/task_manager_performance/kibana.json create mode 100644 x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/index.ts create mode 100644 x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/init_routes.ts create mode 100644 x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/plugin.ts create mode 100644 x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/types.ts diff --git a/renovate.json5 b/renovate.json5 index 61b2485ecf44b0..c4efa86366bf40 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -771,6 +771,14 @@ '@types/podium', ], }, + { + groupSlug: 'pretty-ms', + groupName: 'pretty-ms related packages', + packageNames: [ + 'pretty-ms', + '@types/pretty-ms', + ], + }, { groupSlug: 'proper-lockfile', groupName: 'proper-lockfile related packages', @@ -864,6 +872,14 @@ '@types/sinon', ], }, + { + groupSlug: 'stats-lite', + groupName: 'stats-lite related packages', + packageNames: [ + 'stats-lite', + '@types/stats-lite', + ], + }, { groupSlug: 'storybook', groupName: 'storybook related packages', diff --git a/test/scripts/jenkins_xpack_build_kibana.sh b/test/scripts/jenkins_xpack_build_kibana.sh index 962d2794f712f4..8dc41639fa9468 100755 --- a/test/scripts/jenkins_xpack_build_kibana.sh +++ b/test/scripts/jenkins_xpack_build_kibana.sh @@ -7,7 +7,9 @@ echo " -> building kibana platform plugins" node scripts/build_kibana_platform_plugins \ --scan-dir "$XPACK_DIR/test/plugin_functional/plugins" \ --scan-dir "$XPACK_DIR/test/functional_with_es_ssl/fixtures/plugins" \ + --scan-dir "$XPACK_DIR/test/alerting_api_integration/plugins" \ --scan-dir "$XPACK_DIR/test/plugin_api_integration/plugins" \ + --scan-dir "$XPACK_DIR/test/plugin_api_perf/plugins" \ --verbose; # doesn't persist, also set in kibanaPipeline.groovy diff --git a/x-pack/index.js b/x-pack/index.js index 99b63a49f57930..bac871fcb5414b 100644 --- a/x-pack/index.js +++ b/x-pack/index.js @@ -12,7 +12,6 @@ import { dashboardMode } from './legacy/plugins/dashboard_mode'; import { beats } from './legacy/plugins/beats_management'; import { maps } from './legacy/plugins/maps'; import { spaces } from './legacy/plugins/spaces'; -import { taskManager } from './legacy/plugins/task_manager'; import { encryptedSavedObjects } from './legacy/plugins/encrypted_saved_objects'; import { ingestManager } from './legacy/plugins/ingest_manager'; @@ -26,7 +25,6 @@ module.exports = function(kibana) { dashboardMode(kibana), beats(kibana), maps(kibana), - taskManager(kibana), encryptedSavedObjects(kibana), ingestManager(kibana), ]; diff --git a/x-pack/legacy/plugins/task_manager/server/index.ts b/x-pack/legacy/plugins/task_manager/server/index.ts deleted file mode 100644 index a3167920efa06c..00000000000000 --- a/x-pack/legacy/plugins/task_manager/server/index.ts +++ /dev/null @@ -1,66 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Root } from 'joi'; -import { Legacy } from 'kibana'; - -import { createLegacyApi, getTaskManagerSetup } from './legacy'; -export { LegacyTaskManagerApi, getTaskManagerSetup, getTaskManagerStart } from './legacy'; - -// Once all plugins are migrated to NP, this can be removed -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { TaskManager } from '../../../../plugins/task_manager/server/task_manager'; -import { - LegacyPluginApi, - LegacyPluginSpec, - ArrayOrItem, -} from '../../../../../src/legacy/plugin_discovery/types'; - -export function taskManager(kibana: LegacyPluginApi): ArrayOrItem { - return new kibana.Plugin({ - id: 'task_manager', - require: ['kibana', 'elasticsearch', 'xpack_main'], - configPrefix: 'xpack.task_manager', - config(Joi: Root) { - return Joi.object({ - enabled: Joi.boolean().default(true), - index: Joi.string() - .description('The name of the index used to store task information.') - .default('.kibana_task_manager') - .invalid(['.tasks']), - }) - .unknown(true) - .default(); - }, - init(server: Legacy.Server) { - /* - * We must expose the New Platform Task Manager Plugin via the legacy Api - * as removing it now would be a breaking change - we'll remove this in v8.0.0 - */ - server.expose( - createLegacyApi( - getTaskManagerSetup(server)! - .registerLegacyAPI() - .then((taskManagerPlugin: TaskManager) => { - // we can't tell the Kibana Platform Task Manager plugin to - // to wait to `start` as that happens before legacy plugins - // instead we will start the internal Task Manager plugin when - // all legacy plugins have finished initializing - // Once all plugins are migrated to NP, this can be removed - - // the typing for the lagcy server isn't quite correct, so - // we'll bypase it for now - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (this as any).kbnServer.afterPluginsInit(() => { - taskManagerPlugin.start(); - }); - return taskManagerPlugin; - }) - ) - ); - }, - } as Legacy.PluginSpecOptions); -} diff --git a/x-pack/legacy/plugins/task_manager/server/legacy.ts b/x-pack/legacy/plugins/task_manager/server/legacy.ts deleted file mode 100644 index 0d50828004a944..00000000000000 --- a/x-pack/legacy/plugins/task_manager/server/legacy.ts +++ /dev/null @@ -1,58 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Server } from 'src/legacy/server/kbn_server'; -import { - TaskManagerSetupContract, - TaskManagerStartContract, -} from '../../../../plugins/task_manager/server'; - -import { Middleware } from '../../../../plugins/task_manager/server/lib/middleware.js'; -import { - TaskDictionary, - TaskInstanceWithDeprecatedFields, - TaskInstanceWithId, - TaskDefinition, -} from '../../../../plugins/task_manager/server/task.js'; -import { SearchOpts } from '../../../../plugins/task_manager/server/task_store.js'; - -// Once all plugins are migrated to NP and we can remove Legacy TaskManager in version 8.0.0, -// this can be removed -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { TaskManager } from '../../../../plugins/task_manager/server/task_manager'; - -export type LegacyTaskManagerApi = Pick< - TaskManagerSetupContract, - 'addMiddleware' | 'registerTaskDefinitions' -> & - TaskManagerStartContract; - -export function getTaskManagerSetup(server: Server): TaskManagerSetupContract | undefined { - return server?.newPlatform?.setup?.plugins?.taskManager as TaskManagerSetupContract; -} - -export function getTaskManagerStart(server: Server): TaskManagerStartContract | undefined { - return server?.newPlatform?.start?.plugins?.taskManager as TaskManagerStartContract; -} - -export function createLegacyApi(legacyTaskManager: Promise): LegacyTaskManagerApi { - return { - addMiddleware: (middleware: Middleware) => { - legacyTaskManager.then((tm: TaskManager) => tm.addMiddleware(middleware)); - }, - registerTaskDefinitions: (taskDefinitions: TaskDictionary) => { - legacyTaskManager.then((tm: TaskManager) => tm.registerTaskDefinitions(taskDefinitions)); - }, - fetch: (opts: SearchOpts) => legacyTaskManager.then((tm: TaskManager) => tm.fetch(opts)), - get: (id: string) => legacyTaskManager.then((tm: TaskManager) => tm.get(id)), - remove: (id: string) => legacyTaskManager.then((tm: TaskManager) => tm.remove(id)), - schedule: (taskInstance: TaskInstanceWithDeprecatedFields, options?: object) => - legacyTaskManager.then((tm: TaskManager) => tm.schedule(taskInstance, options)), - runNow: (taskId: string) => legacyTaskManager.then((tm: TaskManager) => tm.runNow(taskId)), - ensureScheduled: (taskInstance: TaskInstanceWithId, options?: object) => - legacyTaskManager.then((tm: TaskManager) => tm.ensureScheduled(taskInstance, options)), - }; -} diff --git a/x-pack/package.json b/x-pack/package.json index be6f436fd3ff76..3a3a0a94fa9eaf 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -115,6 +115,8 @@ "@types/uuid": "^3.4.4", "@types/xml-crypto": "^1.4.0", "@types/xml2js": "^0.4.5", + "@types/stats-lite": "^2.2.0", + "@types/pretty-ms": "^5.0.0", "@welldone-software/why-did-you-render": "^4.0.0", "abab": "^1.0.4", "axios": "^0.19.0", diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index a6cc1fb5463bbb..f14df794bbf47b 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -177,15 +177,15 @@ export class ActionsPlugin implements Plugin, Plugi const usageCollection = plugins.usageCollection; if (usageCollection) { + initializeActionsTelemetry( + this.telemetryLogger, + plugins.taskManager, + core, + await this.kibanaIndex + ); + core.getStartServices().then(async ([, startPlugins]) => { registerActionsUsageCollector(usageCollection, startPlugins.taskManager); - - initializeActionsTelemetry( - this.telemetryLogger, - plugins.taskManager, - core, - await this.kibanaIndex - ); }); } diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index 8cdde2eeb9877a..7bd515616a3c11 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -152,15 +152,15 @@ export class AlertingPlugin { const usageCollection = plugins.usageCollection; if (usageCollection) { + initializeAlertingTelemetry( + this.telemetryLogger, + core, + plugins.taskManager, + await this.kibanaIndex + ); + core.getStartServices().then(async ([, startPlugins]) => { registerAlertsUsageCollector(usageCollection, startPlugins.taskManager); - - initializeAlertingTelemetry( - this.telemetryLogger, - core, - plugins.taskManager, - await this.kibanaIndex - ); }); } diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts index 3eb61bb1307258..c4b5dc868c37ee 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts @@ -38,6 +38,21 @@ export async function createApmTelemetry({ taskManager: TaskManagerSetupContract; logger: Logger; }) { + taskManager.registerTaskDefinitions({ + [APM_TELEMETRY_TASK_NAME]: { + title: 'Collect APM telemetry', + type: APM_TELEMETRY_TASK_NAME, + createTaskRunner: () => { + return { + run: async () => { + await collectAndStore(); + }, + cancel: async () => {} + }; + } + } + }); + const savedObjectsClient = await getInternalSavedObjectsClient(core); const collectAndStore = async () => { @@ -79,21 +94,6 @@ export async function createApmTelemetry({ ); }; - taskManager.registerTaskDefinitions({ - [APM_TELEMETRY_TASK_NAME]: { - title: 'Collect APM telemetry', - type: APM_TELEMETRY_TASK_NAME, - createTaskRunner: () => { - return { - run: async () => { - await collectAndStore(); - }, - cancel: async () => {} - }; - } - } - }); - const collector = usageCollector.makeUsageCollector({ type: 'apm', fetch: async () => { diff --git a/x-pack/plugins/task_manager/server/create_task_manager.test.ts b/x-pack/plugins/task_manager/server/create_task_manager.test.ts deleted file mode 100644 index 133cfcac4c046e..00000000000000 --- a/x-pack/plugins/task_manager/server/create_task_manager.test.ts +++ /dev/null @@ -1,62 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createTaskManager, LegacyDeps } from './create_task_manager'; -import { mockLogger } from './test_utils'; -import { CoreSetup, SavedObjectsSerializer, UuidServiceSetup } from '../../../../src/core/server'; -import { - savedObjectsRepositoryMock, - savedObjectsTypeRegistryMock, -} from '../../../../src/core/server/mocks'; - -jest.mock('./task_manager'); - -describe('createTaskManager', () => { - const uuid: UuidServiceSetup = { - getInstanceUuid() { - return 'some-uuid'; - }, - }; - const mockCoreSetup = { - uuid, - } as CoreSetup; - - const getMockLegacyDeps = (): LegacyDeps => ({ - config: {}, - savedObjectsSerializer: new SavedObjectsSerializer(savedObjectsTypeRegistryMock.create()), - elasticsearch: { - callAsInternalUser: jest.fn(), - }, - savedObjectsRepository: savedObjectsRepositoryMock.create(), - logger: mockLogger(), - }); - - beforeEach(() => { - jest.resetAllMocks(); - }); - - test('exposes the underlying TaskManager', async () => { - const mockLegacyDeps = getMockLegacyDeps(); - const setupResult = createTaskManager(mockCoreSetup, mockLegacyDeps); - expect(setupResult).toMatchInlineSnapshot(` - TaskManager { - "addMiddleware": [MockFunction], - "assertUninitialized": [MockFunction], - "attemptToRun": [MockFunction], - "ensureScheduled": [MockFunction], - "fetch": [MockFunction], - "get": [MockFunction], - "registerTaskDefinitions": [MockFunction], - "remove": [MockFunction], - "runNow": [MockFunction], - "schedule": [MockFunction], - "start": [MockFunction], - "stop": [MockFunction], - "waitUntilStarted": [MockFunction], - } - `); - }); -}); diff --git a/x-pack/plugins/task_manager/server/create_task_manager.ts b/x-pack/plugins/task_manager/server/create_task_manager.ts deleted file mode 100644 index 7ab6acba7976dd..00000000000000 --- a/x-pack/plugins/task_manager/server/create_task_manager.ts +++ /dev/null @@ -1,43 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - IClusterClient, - SavedObjectsSerializer, - CoreSetup, - ISavedObjectsRepository, -} from '../../../../src/core/server'; -import { TaskManager } from './task_manager'; -import { Logger } from './types'; -import { TaskManagerConfig } from './config'; - -export interface LegacyDeps { - config: unknown; - elasticsearch: Pick; - savedObjectsRepository: ISavedObjectsRepository; - savedObjectsSerializer: SavedObjectsSerializer; - logger: Logger; -} - -export function createTaskManager( - core: CoreSetup, - { - logger, - config, - elasticsearch: { callAsInternalUser }, - savedObjectsRepository, - savedObjectsSerializer, - }: LegacyDeps -) { - return new TaskManager({ - taskManagerId: core.uuid.getInstanceUuid(), - config: config as TaskManagerConfig, - savedObjectsRepository, - serializer: savedObjectsSerializer, - callAsInternalUser, - logger, - }); -} diff --git a/x-pack/plugins/task_manager/server/mocks.ts b/x-pack/plugins/task_manager/server/mocks.ts index 8ec05dd1bd401d..4a78a0b49001b2 100644 --- a/x-pack/plugins/task_manager/server/mocks.ts +++ b/x-pack/plugins/task_manager/server/mocks.ts @@ -8,7 +8,6 @@ import { TaskManagerSetupContract, TaskManagerStartContract } from './plugin'; const createSetupMock = () => { const mock: jest.Mocked = { - registerLegacyAPI: jest.fn(), addMiddleware: jest.fn(), registerTaskDefinitions: jest.fn(), }; diff --git a/x-pack/plugins/task_manager/server/plugin.ts b/x-pack/plugins/task_manager/server/plugin.ts index 0f6e3fc31d96d6..4295dbf912c4cb 100644 --- a/x-pack/plugins/task_manager/server/plugin.ts +++ b/x-pack/plugins/task_manager/server/plugin.ts @@ -3,20 +3,19 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { PluginInitializerContext, Plugin, CoreSetup } from 'src/core/server'; +import { PluginInitializerContext, Plugin, CoreSetup, CoreStart } from 'src/core/server'; import { Subject } from 'rxjs'; import { first } from 'rxjs/operators'; -import { once } from 'lodash'; import { TaskDictionary, TaskDefinition } from './task'; import { TaskManager } from './task_manager'; -import { createTaskManager } from './create_task_manager'; import { TaskManagerConfig } from './config'; import { Middleware } from './lib/middleware'; import { setupSavedObjects } from './saved_objects'; -export type TaskManagerSetupContract = { - registerLegacyAPI: () => Promise; -} & Pick; +export type TaskManagerSetupContract = Pick< + TaskManager, + 'addMiddleware' | 'registerTaskDefinitions' +>; export type TaskManagerStartContract = Pick< TaskManager, @@ -28,39 +27,24 @@ export class TaskManagerPlugin legacyTaskManager$: Subject = new Subject(); taskManager: Promise = this.legacyTaskManager$.pipe(first()).toPromise(); currentConfig: TaskManagerConfig; + taskManagerId?: string; + config?: TaskManagerConfig; constructor(private readonly initContext: PluginInitializerContext) { this.initContext = initContext; this.currentConfig = {} as TaskManagerConfig; } - public async setup(core: CoreSetup, plugins: unknown): Promise { - const logger = this.initContext.logger.get('taskManager'); - const config = await this.initContext.config + public async setup(core: CoreSetup): Promise { + this.config = await this.initContext.config .create() .pipe(first()) .toPromise(); - setupSavedObjects(core.savedObjects, config); + setupSavedObjects(core.savedObjects, this.config); + this.taskManagerId = core.uuid.getInstanceUuid(); return { - registerLegacyAPI: once(() => { - (async () => { - const [{ savedObjects, elasticsearch }] = await core.getStartServices(); - const savedObjectsRepository = savedObjects.createInternalRepository(['task']); - this.legacyTaskManager$.next( - createTaskManager(core, { - logger, - config, - elasticsearch: elasticsearch.legacy.client, - savedObjectsRepository, - savedObjectsSerializer: savedObjects.createSerializer(), - }) - ); - this.legacyTaskManager$.complete(); - })(); - return this.taskManager; - }), addMiddleware: (middleware: Middleware) => { this.taskManager.then(tm => tm.addMiddleware(middleware)); }, @@ -70,7 +54,29 @@ export class TaskManagerPlugin }; } - public start(): TaskManagerStartContract { + public start({ savedObjects, elasticsearch }: CoreStart): TaskManagerStartContract { + const logger = this.initContext.logger.get('taskManager'); + const savedObjectsRepository = savedObjects.createInternalRepository(['task']); + + this.legacyTaskManager$.next( + new TaskManager({ + taskManagerId: this.taskManagerId!, + config: this.config!, + savedObjectsRepository, + serializer: savedObjects.createSerializer(), + callAsInternalUser: elasticsearch.legacy.client.callAsInternalUser, + logger, + }) + ); + this.legacyTaskManager$.complete(); + + // we need to "drain" any calls made to the seup API + // before `starting` TaskManager. This is a legacy relic + // of the old API that should be resolved once we split + // Task manager into two services, setup and start, instead + // of the single instance of TaskManager + this.taskManager.then(tm => tm.start()); + return { fetch: (...args) => this.taskManager.then(tm => tm.fetch(...args)), get: (...args) => this.taskManager.then(tm => tm.get(...args)), diff --git a/x-pack/plugins/task_manager/server/task_manager.mock.ts b/x-pack/plugins/task_manager/server/task_manager.mock.ts index 1be1a81cdeb688..1fc626e7d58d64 100644 --- a/x-pack/plugins/task_manager/server/task_manager.mock.ts +++ b/x-pack/plugins/task_manager/server/task_manager.mock.ts @@ -11,7 +11,6 @@ export const taskManagerMock = { const mocked: jest.Mocked = { registerTaskDefinitions: jest.fn(), addMiddleware: jest.fn(), - registerLegacyAPI: jest.fn(), ...overrides, }; return mocked; diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/servicenow.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/servicenow.ts index 1244657ed99889..0bb98a540c4e84 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/servicenow.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/servicenow.ts @@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators'; +} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // node ../scripts/functional_test_runner.js --grep "Actions.servicenddd" --config=test/alerting_api_integration/security_and_spaces/config.ts diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/slack.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/slack.ts index 4151deab45213d..15b7f4f8b33867 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/slack.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/slack.ts @@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators'; +} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function slackTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/webhook.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/webhook.ts index bae6dada48fb7e..e3ba075d27de25 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/webhook.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/webhook.ts @@ -8,7 +8,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators'; +} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function webhookTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index 72a2774e672f1b..d6b5b40d99d999 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -5,10 +5,11 @@ */ import path from 'path'; +import fs from 'fs'; import { CA_CERT_PATH } from '@kbn/dev-utils'; import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; import { services } from './services'; -import { getAllExternalServiceSimulatorPaths } from './fixtures/plugins/actions_simulators'; +import { getAllExternalServiceSimulatorPaths } from './fixtures/plugins/actions_simulators/server/plugin'; interface CreateTestConfigOptions { license: string; @@ -48,6 +49,11 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) protocol: ssl ? 'https' : 'http', }, }; + // Find all folders in ./plugins since we treat all them as plugin folder + const allFiles = fs.readdirSync(path.resolve(__dirname, 'fixtures', 'plugins')); + const plugins = allFiles.filter(file => + fs.statSync(path.resolve(__dirname, 'fixtures', 'plugins', file)).isDirectory() + ); return { testFiles: [require.resolve(`../${name}/tests/`)], @@ -123,10 +129,10 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) }, ])}`, ...disabledPlugins.map(key => `--xpack.${key}.enabled=false`), - `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'alerts')}`, - `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'actions_simulators')}`, - `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'task_manager')}`, - `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'aad')}`, + ...plugins.map( + pluginDir => + `--plugin-path=${path.resolve(__dirname, 'fixtures', 'plugins', pluginDir)}` + ), `--server.xsrf.whitelist=${JSON.stringify(getAllExternalServiceSimulatorPaths())}`, ...(ssl ? [ diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/index.ts deleted file mode 100644 index 400aec7e11c8de..00000000000000 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/index.ts +++ /dev/null @@ -1,59 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import Joi from 'joi'; -import Hapi from 'hapi'; -import { Legacy } from 'kibana'; -import KbnServer from '../../../../../../../src/legacy/server/kbn_server'; -import { EncryptedSavedObjectsPluginStart } from '../../../../../../plugins/encrypted_saved_objects/server'; - -interface CheckAADRequest extends Hapi.Request { - payload: { - spaceId?: string; - type: string; - id: string; - }; -} - -// eslint-disable-next-line import/no-default-export -export default function(kibana: any) { - return new kibana.Plugin({ - require: ['encryptedSavedObjects'], - name: 'aad-fixtures', - init(server: Legacy.Server) { - const newPlatform = ((server as unknown) as KbnServer).newPlatform; - const esoPlugin = newPlatform.start.plugins - .encryptedSavedObjects as EncryptedSavedObjectsPluginStart; - - server.route({ - method: 'POST', - path: '/api/check_aad', - options: { - validate: { - payload: Joi.object() - .keys({ - spaceId: Joi.string().optional(), - type: Joi.string().required(), - id: Joi.string().required(), - }) - .required(), - }, - }, - handler: (async (request: CheckAADRequest) => { - let namespace: string | undefined; - const spacesPlugin = server.plugins.spaces; - if (spacesPlugin && request.payload.spaceId) { - namespace = spacesPlugin.spaceIdToNamespace(request.payload.spaceId); - } - await esoPlugin.getDecryptedAsInternalUser(request.payload.type, request.payload.id, { - namespace, - }); - return { success: true }; - }) as Hapi.Lifecycle.Method, - }); - }, - }); -} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/kibana.json b/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/kibana.json new file mode 100644 index 00000000000000..9a7bedbb5c6d5c --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "aad-fixtures", + "version": "1.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack"], + "requiredPlugins": ["taskManager", "encryptedSavedObjects"], + "optionalPlugins": ["spaces"], + "server": true, + "ui": false +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/package.json b/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/package.json index f5d174c18a2099..128b75aaa0509b 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/package.json +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/package.json @@ -1,7 +1,20 @@ { "name": "aad-fixtures", - "version": "0.0.0", + "version": "1.0.0", "kibana": { - "version": "kibana" + "version": "kibana", + "templateVersion": "1.0.0" + }, + "main": "target/test/plugin_api_integration/plugins/aad", + "scripts": { + "kbn": "node ../../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "3.7.2" + }, + "license": "Apache-2.0", + "dependencies": { + "joi": "^13.5.2" } } diff --git a/x-pack/legacy/plugins/task_manager/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/server/index.ts similarity index 72% rename from x-pack/legacy/plugins/task_manager/index.ts rename to x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/server/index.ts index 276d1ea3accea6..54d6de50cff4d6 100644 --- a/x-pack/legacy/plugins/task_manager/index.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/server/index.ts @@ -4,4 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './server/'; +import { FixturePlugin } from './plugin'; + +export const plugin = () => new FixturePlugin(); diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/server/plugin.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/server/plugin.ts new file mode 100644 index 00000000000000..0e9c71d8c20c81 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/server/plugin.ts @@ -0,0 +1,64 @@ +/* + * 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 { + Plugin, + CoreSetup, + RequestHandlerContext, + KibanaRequest, + KibanaResponseFactory, + IKibanaResponse, +} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { EncryptedSavedObjectsPluginStart } from '../../../../../../../plugins/encrypted_saved_objects/server'; +import { SpacesPluginSetup } from '../../../../../../../plugins/spaces/server'; + +interface FixtureSetupDeps { + spaces?: SpacesPluginSetup; +} + +interface FixtureStartDeps { + encryptedSavedObjects: EncryptedSavedObjectsPluginStart; +} + +export class FixturePlugin implements Plugin { + public setup(core: CoreSetup, { spaces }: FixtureSetupDeps) { + core.http.createRouter().post( + { + path: '/api/check_aad', + validate: { + body: schema.object({ + spaceId: schema.maybe(schema.string()), + type: schema.string(), + id: schema.string(), + }), + }, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + try { + let namespace: string | undefined; + const [, { encryptedSavedObjects }] = await core.getStartServices(); + if (spaces && req.body.spaceId) { + namespace = spaces.spacesService.spaceIdToNamespace(req.body.spaceId); + } + await encryptedSavedObjects.getDecryptedAsInternalUser(req.body.type, req.body.id, { + namespace, + }); + return res.ok({ body: { success: true } }); + } catch (err) { + return res.internalError({ body: err }); + } + } + ); + } + + public start() {} + public stop() {} +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/index.ts deleted file mode 100644 index 6e7e9e37937786..00000000000000 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/index.ts +++ /dev/null @@ -1,91 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import Hapi from 'hapi'; -import { PluginSetupContract as ActionsPluginSetupContract } from '../../../../../../plugins/actions/server/plugin'; -import { ActionType } from '../../../../../../plugins/actions/server'; - -import { initPlugin as initPagerduty } from './pagerduty_simulation'; -import { initPlugin as initSlack } from './slack_simulation'; -import { initPlugin as initWebhook } from './webhook_simulation'; -import { initPlugin as initServiceNow } from './servicenow_simulation'; -import { initPlugin as initJira } from './jira_simulation'; - -const NAME = 'actions-FTS-external-service-simulators'; - -export enum ExternalServiceSimulator { - PAGERDUTY = 'pagerduty', - SERVICENOW = 'servicenow', - JIRA = 'jira', - SLACK = 'slack', - WEBHOOK = 'webhook', -} - -export function getExternalServiceSimulatorPath(service: ExternalServiceSimulator): string { - return `/api/_${NAME}/${service}`; -} - -export function getAllExternalServiceSimulatorPaths(): string[] { - const allPaths = Object.values(ExternalServiceSimulator).map(service => - getExternalServiceSimulatorPath(service) - ); - - allPaths.push(`/api/_${NAME}/${ExternalServiceSimulator.SERVICENOW}/api/now/v2/table/incident`); - allPaths.push(`/api/_${NAME}/${ExternalServiceSimulator.JIRA}/rest/api/2/issue`); - return allPaths; -} - -// eslint-disable-next-line import/no-default-export -export default function(kibana: any) { - return new kibana.Plugin({ - require: ['xpack_main'], - name: NAME, - init: (server: Hapi.Server) => { - // this action is specifically NOT enabled in ../../config.ts - const notEnabledActionType: ActionType = { - id: 'test.not-enabled', - name: 'Test: Not Enabled', - minimumLicenseRequired: 'gold', - async executor() { - return { status: 'ok', actionId: '' }; - }, - }; - (server.newPlatform.setup.plugins.actions as ActionsPluginSetupContract).registerType( - notEnabledActionType - ); - server.plugins.xpack_main.registerFeature({ - id: 'actions', - name: 'Actions', - app: ['actions', 'kibana'], - privileges: { - all: { - app: ['actions', 'kibana'], - savedObject: { - all: ['action', 'action_task_params'], - read: [], - }, - ui: [], - api: ['actions-read', 'actions-all'], - }, - read: { - app: ['actions', 'kibana'], - savedObject: { - all: ['action_task_params'], - read: ['action'], - }, - ui: [], - api: ['actions-read'], - }, - }, - }); - - initPagerduty(server, getExternalServiceSimulatorPath(ExternalServiceSimulator.PAGERDUTY)); - initSlack(server, getExternalServiceSimulatorPath(ExternalServiceSimulator.SLACK)); - initWebhook(server, getExternalServiceSimulatorPath(ExternalServiceSimulator.WEBHOOK)); - initServiceNow(server, getExternalServiceSimulatorPath(ExternalServiceSimulator.SERVICENOW)); - initJira(server, getExternalServiceSimulatorPath(ExternalServiceSimulator.JIRA)); - }, - }); -} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/jira_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/jira_simulation.ts deleted file mode 100644 index 629d0197b22923..00000000000000 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/jira_simulation.ts +++ /dev/null @@ -1,101 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; - -interface JiraRequest extends Hapi.Request { - payload: { - summary: string; - description?: string; - comments?: string; - }; -} -export function initPlugin(server: Hapi.Server, path: string) { - server.route({ - method: 'POST', - path: `${path}/rest/api/2/issue`, - options: { - auth: false, - }, - handler: createHandler as Hapi.Lifecycle.Method, - }); - - server.route({ - method: 'PUT', - path: `${path}/rest/api/2/issue/{id}`, - options: { - auth: false, - }, - handler: updateHandler as Hapi.Lifecycle.Method, - }); - - server.route({ - method: 'GET', - path: `${path}/rest/api/2/issue/{id}`, - options: { - auth: false, - }, - handler: getHandler as Hapi.Lifecycle.Method, - }); - - server.route({ - method: 'POST', - path: `${path}/rest/api/2/issue/{id}/comment`, - options: { - auth: false, - }, - handler: createCommentHanlder as Hapi.Lifecycle.Method, - }); -} - -// ServiceNow simulator: create a servicenow action pointing here, and you can get -// different responses based on the message posted. See the README.md for -// more info. -function createHandler(request: JiraRequest, h: any) { - return jsonResponse(h, 200, { - id: '123', - key: 'CK-1', - created: '2020-04-27T14:17:45.490Z', - }); -} - -function updateHandler(request: JiraRequest, h: any) { - return jsonResponse(h, 200, { - id: '123', - key: 'CK-1', - created: '2020-04-27T14:17:45.490Z', - updated: '2020-04-27T14:17:45.490Z', - }); -} - -function getHandler(request: JiraRequest, h: any) { - return jsonResponse(h, 200, { - id: '123', - key: 'CK-1', - created: '2020-04-27T14:17:45.490Z', - updated: '2020-04-27T14:17:45.490Z', - summary: 'title', - description: 'description', - }); -} - -function createCommentHanlder(request: JiraRequest, h: any) { - return jsonResponse(h, 200, { - id: '123', - created: '2020-04-27T14:17:45.490Z', - }); -} - -function jsonResponse(h: any, code: number, object?: any) { - if (object == null) { - return h.response('').code(code); - } - - return h - .response(JSON.stringify(object)) - .type('application/json') - .code(code); -} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/kibana.json b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/kibana.json new file mode 100644 index 00000000000000..5f92b9e5479e84 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "actions_simulators", + "version": "1.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack"], + "requiredPlugins": ["actions", "features", "encryptedSavedObjects"], + "server": true, + "ui": false +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/package.json b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/package.json index 7f319f52dee2d1..b1076437ef37fc 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/package.json +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/package.json @@ -1,7 +1,20 @@ { - "name": "actions-fixtures", - "version": "0.0.0", + "name": "actions_simulators", + "version": "1.0.0", "kibana": { - "version": "kibana" + "version": "kibana", + "templateVersion": "1.0.0" + }, + "main": "target/test/plugin_api_integration/plugins/actions_simulators", + "scripts": { + "kbn": "node ../../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "3.7.2" + }, + "license": "Apache-2.0", + "dependencies": { + "joi": "^13.5.2" } } diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/pagerduty_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/pagerduty_simulation.ts deleted file mode 100644 index 1de1476fc4ff2e..00000000000000 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/pagerduty_simulation.ts +++ /dev/null @@ -1,79 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import Joi from 'joi'; -import Hapi from 'hapi'; - -interface PagerdutyRequest extends Hapi.Request { - payload: { - dedup_key: string; - payload: { - summary: string; - }; - }; -} - -export function initPlugin(server: Hapi.Server, path: string) { - server.route({ - method: 'POST', - path, - options: { - auth: false, - validate: { - options: { abortEarly: false }, - payload: Joi.object() - .unknown(true) - .keys({ - dedup_key: Joi.string(), - payload: Joi.object() - .unknown(true) - .keys({ - summary: Joi.string(), - }), - }), - }, - }, - handler: pagerdutyHandler as Hapi.Lifecycle.Method, - }); -} -// Pagerduty simulator: create an action pointing here, and you can get -// different responses based on the message posted. See the README.md for -// more info. -function pagerdutyHandler(request: PagerdutyRequest, h: any) { - const body = request.payload; - let dedupKey = body && body.dedup_key; - const summary = body && body.payload && body.payload.summary; - - if (dedupKey == null) { - dedupKey = `kibana-ft-simulator-dedup-key-${new Date().toISOString()}`; - } - - switch (summary) { - case 'respond-with-429': - return jsonResponse(h, 429); - case 'respond-with-502': - return jsonResponse(h, 502); - case 'respond-with-418': - return jsonResponse(h, 418); - } - - return jsonResponse(h, 202, { - status: 'success', - message: 'Event processed', - dedup_key: dedupKey, - }); -} - -function jsonResponse(h: any, code: number, object?: any) { - if (object == null) { - return h.response('').code(code); - } - - return h - .response(JSON.stringify(object)) - .type('application/json') - .code(code); -} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/README.md b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/README.md similarity index 100% rename from x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/README.md rename to x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/README.md diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/index.ts new file mode 100644 index 00000000000000..54d6de50cff4d6 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/index.ts @@ -0,0 +1,9 @@ +/* + * 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 { FixturePlugin } from './plugin'; + +export const plugin = () => new FixturePlugin(); diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/jira_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/jira_simulation.ts new file mode 100644 index 00000000000000..8a55cf6b35652a --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/jira_simulation.ts @@ -0,0 +1,116 @@ +/* + * 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. + */ + +/* + * 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 { + RequestHandlerContext, + KibanaRequest, + KibanaResponseFactory, + IKibanaResponse, + IRouter, +} from 'kibana/server'; + +export function initPlugin(router: IRouter, path: string) { + router.post( + { + path: `${path}/rest/api/2/issue`, + options: { + authRequired: false, + }, + validate: {}, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + return jsonResponse(res, 200, { + id: '123', + key: 'CK-1', + created: '2020-04-27T14:17:45.490Z', + }); + } + ); + + router.put( + { + path: `${path}/rest/api/2/issue/{id}`, + options: { + authRequired: false, + }, + validate: {}, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + return jsonResponse(res, 200, { + id: '123', + key: 'CK-1', + created: '2020-04-27T14:17:45.490Z', + updated: '2020-04-27T14:17:45.490Z', + }); + } + ); + + router.get( + { + path: `${path}/rest/api/2/issue/{id}`, + options: { + authRequired: false, + }, + validate: {}, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + return jsonResponse(res, 200, { + id: '123', + key: 'CK-1', + created: '2020-04-27T14:17:45.490Z', + updated: '2020-04-27T14:17:45.490Z', + summary: 'title', + description: 'description', + }); + } + ); + + router.post( + { + path: `${path}/rest/api/2/issue/{id}/comment`, + options: { + authRequired: false, + }, + validate: {}, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + return jsonResponse(res, 200, { + id: '123', + created: '2020-04-27T14:17:45.490Z', + }); + } + ); +} + +function jsonResponse( + res: KibanaResponseFactory, + code: number, + object: Record = {} +) { + return res.custom>({ body: object, statusCode: code }); +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/pagerduty_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/pagerduty_simulation.ts new file mode 100644 index 00000000000000..83022ec0e3260a --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/pagerduty_simulation.ts @@ -0,0 +1,86 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { + RequestHandlerContext, + KibanaRequest, + KibanaResponseFactory, + IKibanaResponse, + IRouter, +} from 'kibana/server'; + +export function initPlugin(router: IRouter, path: string) { + router.post( + { + path, + options: { + authRequired: false, + }, + validate: { + body: schema.object( + { + dedup_key: schema.string(), + payload: schema.object( + { + summary: schema.string(), + }, + { + unknowns: 'allow', + } + ), + }, + { + unknowns: 'allow', + } + ), + }, + }, + // Pagerduty simulator: create an action pointing here, and you can get + // different responses based on the message posted. See the README.md for + // more info. + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + const { body } = req; + let dedupKey = body && body.dedup_key; + const summary = body && body.payload && body.payload.summary; + + if (dedupKey == null) { + dedupKey = `kibana-ft-simulator-dedup-key-${new Date().toISOString()}`; + } + + switch (summary) { + case 'respond-with-429': + return jsonErrorResponse(res, 429, new Error(summary)); + case 'respond-with-502': + return jsonErrorResponse(res, 502, new Error(summary)); + case 'respond-with-418': + return jsonErrorResponse(res, 418, new Error(summary)); + } + + return jsonResponse(res, 202, { + status: 'success', + message: 'Event processed', + dedup_key: dedupKey, + }); + } + ); +} + +function jsonResponse( + res: KibanaResponseFactory, + code: number, + object: Record = {} +) { + return res.custom>({ body: object, statusCode: code }); +} + +function jsonErrorResponse(res: KibanaResponseFactory, code: number, object: Error) { + return res.custom({ body: object, statusCode: code }); +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin.ts new file mode 100644 index 00000000000000..efb219fc8e7176 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin.ts @@ -0,0 +1,95 @@ +/* + * 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 { Plugin, CoreSetup, IRouter } from 'kibana/server'; +import { EncryptedSavedObjectsPluginStart } from '../../../../../../../plugins/encrypted_saved_objects/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../../plugins/features/server'; +import { PluginSetupContract as ActionsPluginSetupContract } from '../../../../../../../plugins/actions/server/plugin'; +import { ActionType } from '../../../../../../../plugins/actions/server'; +import { initPlugin as initPagerduty } from './pagerduty_simulation'; +import { initPlugin as initServiceNow } from './servicenow_simulation'; +import { initPlugin as initJira } from './jira_simulation'; + +export const NAME = 'actions-FTS-external-service-simulators'; + +export enum ExternalServiceSimulator { + PAGERDUTY = 'pagerduty', + SERVICENOW = 'servicenow', + SLACK = 'slack', + JIRA = 'jira', + WEBHOOK = 'webhook', +} + +export function getExternalServiceSimulatorPath(service: ExternalServiceSimulator): string { + return `/api/_${NAME}/${service}`; +} + +export function getAllExternalServiceSimulatorPaths(): string[] { + const allPaths = Object.values(ExternalServiceSimulator).map(service => + getExternalServiceSimulatorPath(service) + ); + allPaths.push(`/api/_${NAME}/${ExternalServiceSimulator.SERVICENOW}/api/now/v2/table/incident`); + allPaths.push(`/api/_${NAME}/${ExternalServiceSimulator.JIRA}/rest/api/2/issue`); + return allPaths; +} + +interface FixtureSetupDeps { + actions: ActionsPluginSetupContract; + features: FeaturesPluginSetup; +} + +interface FixtureStartDeps { + encryptedSavedObjects: EncryptedSavedObjectsPluginStart; +} + +export class FixturePlugin implements Plugin { + public setup(core: CoreSetup, { features, actions }: FixtureSetupDeps) { + // this action is specifically NOT enabled in ../../config.ts + const notEnabledActionType: ActionType = { + id: 'test.not-enabled', + name: 'Test: Not Enabled', + minimumLicenseRequired: 'gold', + async executor() { + return { status: 'ok', actionId: '' }; + }, + }; + actions.registerType(notEnabledActionType); + features.registerFeature({ + id: 'actions', + name: 'Actions', + app: ['actions', 'kibana'], + privileges: { + all: { + app: ['actions', 'kibana'], + savedObject: { + all: ['action', 'action_task_params'], + read: [], + }, + ui: [], + api: ['actions-read', 'actions-all'], + }, + read: { + app: ['actions', 'kibana'], + savedObject: { + all: ['action_task_params'], + read: ['action'], + }, + ui: [], + api: ['actions-read'], + }, + }, + }); + + const router: IRouter = core.http.createRouter(); + + initPagerduty(router, getExternalServiceSimulatorPath(ExternalServiceSimulator.PAGERDUTY)); + initServiceNow(router, getExternalServiceSimulatorPath(ExternalServiceSimulator.SERVICENOW)); + initJira(router, getExternalServiceSimulatorPath(ExternalServiceSimulator.JIRA)); + } + + public start() {} + public stop() {} +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/servicenow_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/servicenow_simulation.ts new file mode 100644 index 00000000000000..4bf8216aed26f2 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/servicenow_simulation.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; +import { + RequestHandlerContext, + KibanaRequest, + KibanaResponseFactory, + IKibanaResponse, + IRouter, +} from 'kibana/server'; + +export function initPlugin(router: IRouter, path: string) { + router.post( + { + path: `${path}/api/now/v2/table/incident`, + options: { + authRequired: false, + }, + validate: { + body: schema.object({ + short_description: schema.string(), + description: schema.maybe(schema.string()), + comments: schema.maybe(schema.string()), + }), + }, + }, + // ServiceNow simulator: create a servicenow action pointing here, and you can get + // different responses based on the message posted. See the README.md for + // more info. + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + return jsonResponse(res, 200, { + result: { sys_id: '123', number: 'INC01', sys_created_on: '2020-03-10 12:24:20' }, + }); + } + ); + + router.patch( + { + path: `${path}/api/now/v2/table/incident/{id}`, + options: { + authRequired: false, + }, + validate: {}, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + return jsonResponse(res, 200, { + result: { sys_id: '123', number: 'INC01', sys_updated_on: '2020-03-10 12:24:20' }, + }); + } + ); + + router.get( + { + path: `${path}/api/now/v2/table/incident/{id}`, + options: { + authRequired: false, + }, + validate: {}, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + return jsonResponse(res, 200, { + result: { + sys_id: '123', + number: 'INC01', + sys_created_on: '2020-03-10 12:24:20', + short_description: 'title', + description: 'description', + }, + }); + } + ); +} + +function jsonResponse(res: KibanaResponseFactory, code: number, object?: Record) { + if (object == null) { + return res.custom({ + statusCode: code, + body: '', + }); + } + + return res.custom>({ body: object, statusCode: code }); +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/servicenow_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/servicenow_simulation.ts deleted file mode 100644 index cc9521369a47df..00000000000000 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/servicenow_simulation.ts +++ /dev/null @@ -1,81 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; - -interface ServiceNowRequest extends Hapi.Request { - payload: { - short_description: string; - description?: string; - comments?: string; - }; -} -export function initPlugin(server: Hapi.Server, path: string) { - server.route({ - method: 'POST', - path: `${path}/api/now/v2/table/incident`, - options: { - auth: false, - }, - handler: createHandler as Hapi.Lifecycle.Method, - }); - - server.route({ - method: 'PATCH', - path: `${path}/api/now/v2/table/incident/{id}`, - options: { - auth: false, - }, - handler: updateHandler as Hapi.Lifecycle.Method, - }); - - server.route({ - method: 'GET', - path: `${path}/api/now/v2/table/incident/{id}`, - options: { - auth: false, - }, - handler: getHandler as Hapi.Lifecycle.Method, - }); -} - -// ServiceNow simulator: create a servicenow action pointing here, and you can get -// different responses based on the message posted. See the README.md for -// more info. -function createHandler(request: ServiceNowRequest, h: any) { - return jsonResponse(h, 200, { - result: { sys_id: '123', number: 'INC01', sys_created_on: '2020-03-10 12:24:20' }, - }); -} - -function updateHandler(request: ServiceNowRequest, h: any) { - return jsonResponse(h, 200, { - result: { sys_id: '123', number: 'INC01', sys_updated_on: '2020-03-10 12:24:20' }, - }); -} - -function getHandler(request: ServiceNowRequest, h: any) { - return jsonResponse(h, 200, { - result: { - sys_id: '123', - number: 'INC01', - sys_created_on: '2020-03-10 12:24:20', - short_description: 'title', - description: 'description', - }, - }); -} - -function jsonResponse(h: any, code: number, object?: any) { - if (object == null) { - return h.response('').code(code); - } - - return h - .response(JSON.stringify(object)) - .type('application/json') - .code(code); -} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators_legacy/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators_legacy/index.ts new file mode 100644 index 00000000000000..4219d99b6c43b3 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators_legacy/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; + * you may not use this file except in compliance with the Elastic License. + */ +import Hapi from 'hapi'; +import { + getExternalServiceSimulatorPath, + NAME, + ExternalServiceSimulator, +} from '../actions_simulators/server/plugin'; + +import { initPlugin as initWebhook } from './webhook_simulation'; +import { initPlugin as initSlack } from './slack_simulation'; + +// eslint-disable-next-line import/no-default-export +export default function(kibana: any) { + return new kibana.Plugin({ + require: ['xpack_main'], + name: `${NAME}-legacy`, + init: (server: Hapi.Server) => { + initWebhook(server, getExternalServiceSimulatorPath(ExternalServiceSimulator.WEBHOOK)); + initSlack(server, getExternalServiceSimulatorPath(ExternalServiceSimulator.SLACK)); + }, + }); +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators_legacy/package.json b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators_legacy/package.json new file mode 100644 index 00000000000000..644cd77d3be753 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators_legacy/package.json @@ -0,0 +1,7 @@ +{ + "name": "actions-fixtures-legacy", + "version": "0.0.0", + "kibana": { + "version": "kibana" + } +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/slack_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators_legacy/slack_simulation.ts similarity index 100% rename from x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/slack_simulation.ts rename to x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators_legacy/slack_simulation.ts diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/webhook_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators_legacy/webhook_simulation.ts similarity index 100% rename from x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/webhook_simulation.ts rename to x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators_legacy/webhook_simulation.ts diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts deleted file mode 100644 index 1a47addf36ab33..00000000000000 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts +++ /dev/null @@ -1,501 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { times } from 'lodash'; -import { schema } from '@kbn/config-schema'; -import { AlertExecutorOptions, AlertType } from '../../../../../../plugins/alerting/server'; -import { ActionTypeExecutorOptions, ActionType } from '../../../../../../plugins/actions/server'; - -// eslint-disable-next-line import/no-default-export -export default function(kibana: any) { - return new kibana.Plugin({ - require: ['xpack_main', 'elasticsearch'], - name: 'alerts-fixture', - init(server: any) { - const clusterClient = server.newPlatform.start.core.elasticsearch.legacy.client; - server.plugins.xpack_main.registerFeature({ - id: 'alerting', - name: 'Alerting', - app: ['alerting', 'kibana'], - privileges: { - all: { - app: ['alerting', 'kibana'], - savedObject: { - all: ['alert'], - read: [], - }, - ui: [], - api: ['alerting-read', 'alerting-all'], - }, - read: { - app: ['alerting', 'kibana'], - savedObject: { - all: [], - read: ['alert'], - }, - ui: [], - api: ['alerting-read'], - }, - }, - }); - - // Action types - const noopActionType: ActionType = { - id: 'test.noop', - name: 'Test: Noop', - minimumLicenseRequired: 'gold', - async executor() { - return { status: 'ok', actionId: '' }; - }, - }; - const indexRecordActionType: ActionType = { - id: 'test.index-record', - name: 'Test: Index Record', - minimumLicenseRequired: 'gold', - validate: { - params: schema.object({ - index: schema.string(), - reference: schema.string(), - message: schema.string(), - }), - config: schema.object({ - unencrypted: schema.string(), - }), - secrets: schema.object({ - encrypted: schema.string(), - }), - }, - async executor({ config, secrets, params, services, actionId }: ActionTypeExecutorOptions) { - await services.callCluster('index', { - index: params.index, - refresh: 'wait_for', - body: { - params, - config, - secrets, - reference: params.reference, - source: 'action:test.index-record', - }, - }); - return { status: 'ok', actionId }; - }, - }; - const failingActionType: ActionType = { - id: 'test.failing', - name: 'Test: Failing', - minimumLicenseRequired: 'gold', - validate: { - params: schema.object({ - index: schema.string(), - reference: schema.string(), - }), - }, - async executor({ config, secrets, params, services }: ActionTypeExecutorOptions) { - await services.callCluster('index', { - index: params.index, - refresh: 'wait_for', - body: { - params, - config, - secrets, - reference: params.reference, - source: 'action:test.failing', - }, - }); - throw new Error(`expected failure for ${params.index} ${params.reference}`); - }, - }; - const rateLimitedActionType: ActionType = { - id: 'test.rate-limit', - name: 'Test: Rate Limit', - minimumLicenseRequired: 'gold', - maxAttempts: 2, - validate: { - params: schema.object({ - index: schema.string(), - reference: schema.string(), - retryAt: schema.number(), - }), - }, - async executor({ config, params, services }: ActionTypeExecutorOptions) { - await services.callCluster('index', { - index: params.index, - refresh: 'wait_for', - body: { - params, - config, - reference: params.reference, - source: 'action:test.rate-limit', - }, - }); - return { - status: 'error', - retry: new Date(params.retryAt), - actionId: '', - }; - }, - }; - const authorizationActionType: ActionType = { - id: 'test.authorization', - name: 'Test: Authorization', - minimumLicenseRequired: 'gold', - validate: { - params: schema.object({ - callClusterAuthorizationIndex: schema.string(), - savedObjectsClientType: schema.string(), - savedObjectsClientId: schema.string(), - index: schema.string(), - reference: schema.string(), - }), - }, - async executor({ params, services, actionId }: ActionTypeExecutorOptions) { - // Call cluster - let callClusterSuccess = false; - let callClusterError; - try { - await services.callCluster('index', { - index: params.callClusterAuthorizationIndex, - refresh: 'wait_for', - body: { - param1: 'test', - }, - }); - callClusterSuccess = true; - } catch (e) { - callClusterError = e; - } - // Call scoped cluster - const callScopedCluster = services.getScopedCallCluster(clusterClient); - let callScopedClusterSuccess = false; - let callScopedClusterError; - try { - await callScopedCluster('index', { - index: params.callClusterAuthorizationIndex, - refresh: 'wait_for', - body: { - param1: 'test', - }, - }); - callScopedClusterSuccess = true; - } catch (e) { - callScopedClusterError = e; - } - // Saved objects client - let savedObjectsClientSuccess = false; - let savedObjectsClientError; - try { - await services.savedObjectsClient.get( - params.savedObjectsClientType, - params.savedObjectsClientId - ); - savedObjectsClientSuccess = true; - } catch (e) { - savedObjectsClientError = e; - } - // Save the result - await services.callCluster('index', { - index: params.index, - refresh: 'wait_for', - body: { - state: { - callClusterSuccess, - callClusterError, - callScopedClusterSuccess, - callScopedClusterError, - savedObjectsClientSuccess, - savedObjectsClientError, - }, - params, - reference: params.reference, - source: 'action:test.authorization', - }, - }); - return { - actionId, - status: 'ok', - }; - }, - }; - server.newPlatform.setup.plugins.actions.registerType(noopActionType); - server.newPlatform.setup.plugins.actions.registerType(indexRecordActionType); - server.newPlatform.setup.plugins.actions.registerType(failingActionType); - server.newPlatform.setup.plugins.actions.registerType(rateLimitedActionType); - server.newPlatform.setup.plugins.actions.registerType(authorizationActionType); - - // Alert types - const alwaysFiringAlertType: AlertType = { - id: 'test.always-firing', - name: 'Test: Always Firing', - actionGroups: [ - { id: 'default', name: 'Default' }, - { id: 'other', name: 'Other' }, - ], - defaultActionGroupId: 'default', - actionVariables: { - state: [{ name: 'instanceStateValue', description: 'the instance state value' }], - context: [{ name: 'instanceContextValue', description: 'the instance context value' }], - }, - async executor(alertExecutorOptions: AlertExecutorOptions) { - const { - services, - params, - state, - alertId, - spaceId, - namespace, - name, - tags, - createdBy, - updatedBy, - } = alertExecutorOptions; - let group = 'default'; - const alertInfo = { alertId, spaceId, namespace, name, tags, createdBy, updatedBy }; - - if (params.groupsToScheduleActionsInSeries) { - const index = state.groupInSeriesIndex || 0; - group = params.groupsToScheduleActionsInSeries[index]; - } - - if (group) { - services - .alertInstanceFactory('1') - .replaceState({ instanceStateValue: true }) - .scheduleActions(group, { - instanceContextValue: true, - }); - } - await services.callCluster('index', { - index: params.index, - refresh: 'wait_for', - body: { - state, - params, - reference: params.reference, - source: 'alert:test.always-firing', - alertInfo, - }, - }); - return { - globalStateValue: true, - groupInSeriesIndex: (state.groupInSeriesIndex || 0) + 1, - }; - }, - }; - // Alert types - const cumulativeFiringAlertType: AlertType = { - id: 'test.cumulative-firing', - name: 'Test: Cumulative Firing', - actionGroups: [ - { id: 'default', name: 'Default' }, - { id: 'other', name: 'Other' }, - ], - defaultActionGroupId: 'default', - async executor(alertExecutorOptions: AlertExecutorOptions) { - const { services, state } = alertExecutorOptions; - const group = 'default'; - - const runCount = (state.runCount || 0) + 1; - - times(runCount, index => { - services - .alertInstanceFactory(`instance-${index}`) - .replaceState({ instanceStateValue: true }) - .scheduleActions(group); - }); - - return { - runCount, - }; - }, - }; - const neverFiringAlertType: AlertType = { - id: 'test.never-firing', - name: 'Test: Never firing', - actionGroups: [ - { - id: 'default', - name: 'Default', - }, - ], - defaultActionGroupId: 'default', - async executor({ services, params, state }: AlertExecutorOptions) { - await services.callCluster('index', { - index: params.index, - refresh: 'wait_for', - body: { - state, - params, - reference: params.reference, - source: 'alert:test.never-firing', - }, - }); - return { - globalStateValue: true, - }; - }, - }; - const failingAlertType: AlertType = { - id: 'test.failing', - name: 'Test: Failing', - actionGroups: [ - { - id: 'default', - name: 'Default', - }, - ], - defaultActionGroupId: 'default', - async executor({ services, params, state }: AlertExecutorOptions) { - await services.callCluster('index', { - index: params.index, - refresh: 'wait_for', - body: { - state, - params, - reference: params.reference, - source: 'alert:test.failing', - }, - }); - throw new Error('Failed to execute alert type'); - }, - }; - const authorizationAlertType: AlertType = { - id: 'test.authorization', - name: 'Test: Authorization', - actionGroups: [ - { - id: 'default', - name: 'Default', - }, - ], - defaultActionGroupId: 'default', - validate: { - params: schema.object({ - callClusterAuthorizationIndex: schema.string(), - savedObjectsClientType: schema.string(), - savedObjectsClientId: schema.string(), - index: schema.string(), - reference: schema.string(), - }), - }, - async executor({ services, params, state }: AlertExecutorOptions) { - // Call cluster - let callClusterSuccess = false; - let callClusterError; - try { - await services.callCluster('index', { - index: params.callClusterAuthorizationIndex, - refresh: 'wait_for', - body: { - param1: 'test', - }, - }); - callClusterSuccess = true; - } catch (e) { - callClusterError = e; - } - // Call scoped cluster - const callScopedCluster = services.getScopedCallCluster(clusterClient); - let callScopedClusterSuccess = false; - let callScopedClusterError; - try { - await callScopedCluster('index', { - index: params.callClusterAuthorizationIndex, - refresh: 'wait_for', - body: { - param1: 'test', - }, - }); - callScopedClusterSuccess = true; - } catch (e) { - callScopedClusterError = e; - } - // Saved objects client - let savedObjectsClientSuccess = false; - let savedObjectsClientError; - try { - await services.savedObjectsClient.get( - params.savedObjectsClientType, - params.savedObjectsClientId - ); - savedObjectsClientSuccess = true; - } catch (e) { - savedObjectsClientError = e; - } - // Save the result - await services.callCluster('index', { - index: params.index, - refresh: 'wait_for', - body: { - state: { - callClusterSuccess, - callClusterError, - callScopedClusterSuccess, - callScopedClusterError, - savedObjectsClientSuccess, - savedObjectsClientError, - }, - params, - reference: params.reference, - source: 'alert:test.authorization', - }, - }); - }, - }; - const validationAlertType: AlertType = { - id: 'test.validation', - name: 'Test: Validation', - actionGroups: [ - { - id: 'default', - name: 'Default', - }, - ], - defaultActionGroupId: 'default', - validate: { - params: schema.object({ - param1: schema.string(), - }), - }, - async executor({ services, params, state }: AlertExecutorOptions) {}, - }; - const noopAlertType: AlertType = { - id: 'test.noop', - name: 'Test: Noop', - actionGroups: [{ id: 'default', name: 'Default' }], - defaultActionGroupId: 'default', - async executor({ services, params, state }: AlertExecutorOptions) {}, - }; - const onlyContextVariablesAlertType: AlertType = { - id: 'test.onlyContextVariables', - name: 'Test: Only Context Variables', - actionGroups: [{ id: 'default', name: 'Default' }], - defaultActionGroupId: 'default', - actionVariables: { - context: [{ name: 'aContextVariable', description: 'this is a context variable' }], - }, - async executor(opts: AlertExecutorOptions) {}, - }; - const onlyStateVariablesAlertType: AlertType = { - id: 'test.onlyStateVariables', - name: 'Test: Only State Variables', - actionGroups: [{ id: 'default', name: 'Default' }], - defaultActionGroupId: 'default', - actionVariables: { - state: [{ name: 'aStateVariable', description: 'this is a state variable' }], - }, - async executor(opts: AlertExecutorOptions) {}, - }; - server.newPlatform.setup.plugins.alerting.registerType(alwaysFiringAlertType); - server.newPlatform.setup.plugins.alerting.registerType(cumulativeFiringAlertType); - server.newPlatform.setup.plugins.alerting.registerType(neverFiringAlertType); - server.newPlatform.setup.plugins.alerting.registerType(failingAlertType); - server.newPlatform.setup.plugins.alerting.registerType(validationAlertType); - server.newPlatform.setup.plugins.alerting.registerType(authorizationAlertType); - server.newPlatform.setup.plugins.alerting.registerType(noopAlertType); - server.newPlatform.setup.plugins.alerting.registerType(onlyContextVariablesAlertType); - server.newPlatform.setup.plugins.alerting.registerType(onlyStateVariablesAlertType); - }, - }); -} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json new file mode 100644 index 00000000000000..98c57db16c9148 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "alerts-fixtures", + "version": "1.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack"], + "requiredPlugins": ["taskManager", "features", "actions", "alerting"], + "optionalPlugins": ["spaces"], + "server": true, + "ui": false +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/package.json b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/package.json index 836fa09855d8fd..53abf490ad3769 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/package.json +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/package.json @@ -1,7 +1,20 @@ { - "name": "alerts", - "version": "0.0.0", + "name": "alerts-fixtures", + "version": "1.0.0", "kibana": { - "version": "kibana" + "version": "kibana", + "templateVersion": "1.0.0" + }, + "main": "target/test/plugin_api_integration/plugins/alerts", + "scripts": { + "kbn": "node ../../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "3.7.2" + }, + "license": "Apache-2.0", + "dependencies": { + "joi": "^13.5.2" } } diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/index.ts new file mode 100644 index 00000000000000..54d6de50cff4d6 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/index.ts @@ -0,0 +1,9 @@ +/* + * 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 { FixturePlugin } from './plugin'; + +export const plugin = () => new FixturePlugin(); diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts new file mode 100644 index 00000000000000..347695dd12bebe --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts @@ -0,0 +1,516 @@ +/* + * 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 { Plugin, CoreSetup } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { times } from 'lodash'; +import { PluginSetupContract as ActionsPluginSetup } from '../../../../../../../plugins/actions/server/plugin'; +import { PluginSetupContract as AlertingPluginSetup } from '../../../../../../../plugins/alerting/server/plugin'; +import { EncryptedSavedObjectsPluginStart } from '../../../../../../../plugins/encrypted_saved_objects/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../../plugins/features/server'; +import { ActionType, ActionTypeExecutorOptions } from '../../../../../../../plugins/actions/server'; +import { AlertType, AlertExecutorOptions } from '../../../../../../../plugins/alerting/server'; + +interface FixtureSetupDeps { + features: FeaturesPluginSetup; + actions: ActionsPluginSetup; + alerting: AlertingPluginSetup; +} + +interface FixtureStartDeps { + encryptedSavedObjects: EncryptedSavedObjectsPluginStart; +} + +export class FixturePlugin implements Plugin { + public setup( + core: CoreSetup, + { features, actions, alerting }: FixtureSetupDeps + ) { + const clusterClient = core.elasticsearch.adminClient; + features.registerFeature({ + id: 'alerting', + name: 'Alerting', + app: ['alerting', 'kibana'], + privileges: { + all: { + app: ['alerting', 'kibana'], + savedObject: { + all: ['alert'], + read: [], + }, + ui: [], + api: ['alerting-read', 'alerting-all'], + }, + read: { + app: ['alerting', 'kibana'], + savedObject: { + all: [], + read: ['alert'], + }, + ui: [], + api: ['alerting-read'], + }, + }, + }); + // Action types + const noopActionType: ActionType = { + id: 'test.noop', + name: 'Test: Noop', + minimumLicenseRequired: 'gold', + async executor() { + return { status: 'ok', actionId: '' }; + }, + }; + const indexRecordActionType: ActionType = { + id: 'test.index-record', + name: 'Test: Index Record', + minimumLicenseRequired: 'gold', + validate: { + params: schema.object({ + index: schema.string(), + reference: schema.string(), + message: schema.string(), + }), + config: schema.object({ + unencrypted: schema.string(), + }), + secrets: schema.object({ + encrypted: schema.string(), + }), + }, + async executor({ config, secrets, params, services, actionId }: ActionTypeExecutorOptions) { + await services.callCluster('index', { + index: params.index, + refresh: 'wait_for', + body: { + params, + config, + secrets, + reference: params.reference, + source: 'action:test.index-record', + }, + }); + return { status: 'ok', actionId }; + }, + }; + const failingActionType: ActionType = { + id: 'test.failing', + name: 'Test: Failing', + minimumLicenseRequired: 'gold', + validate: { + params: schema.object({ + index: schema.string(), + reference: schema.string(), + }), + }, + async executor({ config, secrets, params, services }: ActionTypeExecutorOptions) { + await services.callCluster('index', { + index: params.index, + refresh: 'wait_for', + body: { + params, + config, + secrets, + reference: params.reference, + source: 'action:test.failing', + }, + }); + throw new Error(`expected failure for ${params.index} ${params.reference}`); + }, + }; + const rateLimitedActionType: ActionType = { + id: 'test.rate-limit', + name: 'Test: Rate Limit', + minimumLicenseRequired: 'gold', + maxAttempts: 2, + validate: { + params: schema.object({ + index: schema.string(), + reference: schema.string(), + retryAt: schema.number(), + }), + }, + async executor({ config, params, services }: ActionTypeExecutorOptions) { + await services.callCluster('index', { + index: params.index, + refresh: 'wait_for', + body: { + params, + config, + reference: params.reference, + source: 'action:test.rate-limit', + }, + }); + return { + status: 'error', + retry: new Date(params.retryAt), + actionId: '', + }; + }, + }; + const authorizationActionType: ActionType = { + id: 'test.authorization', + name: 'Test: Authorization', + minimumLicenseRequired: 'gold', + validate: { + params: schema.object({ + callClusterAuthorizationIndex: schema.string(), + savedObjectsClientType: schema.string(), + savedObjectsClientId: schema.string(), + index: schema.string(), + reference: schema.string(), + }), + }, + async executor({ params, services, actionId }: ActionTypeExecutorOptions) { + // Call cluster + let callClusterSuccess = false; + let callClusterError; + try { + await services.callCluster('index', { + index: params.callClusterAuthorizationIndex, + refresh: 'wait_for', + body: { + param1: 'test', + }, + }); + callClusterSuccess = true; + } catch (e) { + callClusterError = e; + } + // Call scoped cluster + const callScopedCluster = services.getScopedCallCluster(clusterClient); + let callScopedClusterSuccess = false; + let callScopedClusterError; + try { + await callScopedCluster('index', { + index: params.callClusterAuthorizationIndex, + refresh: 'wait_for', + body: { + param1: 'test', + }, + }); + callScopedClusterSuccess = true; + } catch (e) { + callScopedClusterError = e; + } + // Saved objects client + let savedObjectsClientSuccess = false; + let savedObjectsClientError; + try { + await services.savedObjectsClient.get( + params.savedObjectsClientType, + params.savedObjectsClientId + ); + savedObjectsClientSuccess = true; + } catch (e) { + savedObjectsClientError = e; + } + // Save the result + await services.callCluster('index', { + index: params.index, + refresh: 'wait_for', + body: { + state: { + callClusterSuccess, + callClusterError, + callScopedClusterSuccess, + callScopedClusterError, + savedObjectsClientSuccess, + savedObjectsClientError, + }, + params, + reference: params.reference, + source: 'action:test.authorization', + }, + }); + return { + actionId, + status: 'ok', + }; + }, + }; + actions.registerType(noopActionType); + actions.registerType(indexRecordActionType); + actions.registerType(failingActionType); + actions.registerType(rateLimitedActionType); + actions.registerType(authorizationActionType); + + const alwaysFiringAlertType: AlertType = { + id: 'test.always-firing', + name: 'Test: Always Firing', + actionGroups: [ + { id: 'default', name: 'Default' }, + { id: 'other', name: 'Other' }, + ], + defaultActionGroupId: 'default', + actionVariables: { + state: [{ name: 'instanceStateValue', description: 'the instance state value' }], + context: [{ name: 'instanceContextValue', description: 'the instance context value' }], + }, + async executor(alertExecutorOptions: AlertExecutorOptions) { + const { + services, + params, + state, + alertId, + spaceId, + namespace, + name, + tags, + createdBy, + updatedBy, + } = alertExecutorOptions; + let group = 'default'; + const alertInfo = { alertId, spaceId, namespace, name, tags, createdBy, updatedBy }; + + if (params.groupsToScheduleActionsInSeries) { + const index = state.groupInSeriesIndex || 0; + group = params.groupsToScheduleActionsInSeries[index]; + } + + if (group) { + services + .alertInstanceFactory('1') + .replaceState({ instanceStateValue: true }) + .scheduleActions(group, { + instanceContextValue: true, + }); + } + await services.callCluster('index', { + index: params.index, + refresh: 'wait_for', + body: { + state, + params, + reference: params.reference, + source: 'alert:test.always-firing', + alertInfo, + }, + }); + return { + globalStateValue: true, + groupInSeriesIndex: (state.groupInSeriesIndex || 0) + 1, + }; + }, + }; + // Alert types + const cumulativeFiringAlertType: AlertType = { + id: 'test.cumulative-firing', + name: 'Test: Cumulative Firing', + actionGroups: [ + { id: 'default', name: 'Default' }, + { id: 'other', name: 'Other' }, + ], + defaultActionGroupId: 'default', + async executor(alertExecutorOptions: AlertExecutorOptions) { + const { services, state } = alertExecutorOptions; + const group = 'default'; + + const runCount = (state.runCount || 0) + 1; + + times(runCount, index => { + services + .alertInstanceFactory(`instance-${index}`) + .replaceState({ instanceStateValue: true }) + .scheduleActions(group); + }); + + return { + runCount, + }; + }, + }; + const neverFiringAlertType: AlertType = { + id: 'test.never-firing', + name: 'Test: Never firing', + actionGroups: [ + { + id: 'default', + name: 'Default', + }, + ], + defaultActionGroupId: 'default', + async executor({ services, params, state }: AlertExecutorOptions) { + await services.callCluster('index', { + index: params.index, + refresh: 'wait_for', + body: { + state, + params, + reference: params.reference, + source: 'alert:test.never-firing', + }, + }); + return { + globalStateValue: true, + }; + }, + }; + const failingAlertType: AlertType = { + id: 'test.failing', + name: 'Test: Failing', + actionGroups: [ + { + id: 'default', + name: 'Default', + }, + ], + defaultActionGroupId: 'default', + async executor({ services, params, state }: AlertExecutorOptions) { + await services.callCluster('index', { + index: params.index, + refresh: 'wait_for', + body: { + state, + params, + reference: params.reference, + source: 'alert:test.failing', + }, + }); + throw new Error('Failed to execute alert type'); + }, + }; + const authorizationAlertType: AlertType = { + id: 'test.authorization', + name: 'Test: Authorization', + actionGroups: [ + { + id: 'default', + name: 'Default', + }, + ], + defaultActionGroupId: 'default', + validate: { + params: schema.object({ + callClusterAuthorizationIndex: schema.string(), + savedObjectsClientType: schema.string(), + savedObjectsClientId: schema.string(), + index: schema.string(), + reference: schema.string(), + }), + }, + async executor({ services, params, state }: AlertExecutorOptions) { + // Call cluster + let callClusterSuccess = false; + let callClusterError; + try { + await services.callCluster('index', { + index: params.callClusterAuthorizationIndex, + refresh: 'wait_for', + body: { + param1: 'test', + }, + }); + callClusterSuccess = true; + } catch (e) { + callClusterError = e; + } + // Call scoped cluster + const callScopedCluster = services.getScopedCallCluster(clusterClient); + let callScopedClusterSuccess = false; + let callScopedClusterError; + try { + await callScopedCluster('index', { + index: params.callClusterAuthorizationIndex, + refresh: 'wait_for', + body: { + param1: 'test', + }, + }); + callScopedClusterSuccess = true; + } catch (e) { + callScopedClusterError = e; + } + // Saved objects client + let savedObjectsClientSuccess = false; + let savedObjectsClientError; + try { + await services.savedObjectsClient.get( + params.savedObjectsClientType, + params.savedObjectsClientId + ); + savedObjectsClientSuccess = true; + } catch (e) { + savedObjectsClientError = e; + } + // Save the result + await services.callCluster('index', { + index: params.index, + refresh: 'wait_for', + body: { + state: { + callClusterSuccess, + callClusterError, + callScopedClusterSuccess, + callScopedClusterError, + savedObjectsClientSuccess, + savedObjectsClientError, + }, + params, + reference: params.reference, + source: 'alert:test.authorization', + }, + }); + }, + }; + const validationAlertType: AlertType = { + id: 'test.validation', + name: 'Test: Validation', + actionGroups: [ + { + id: 'default', + name: 'Default', + }, + ], + defaultActionGroupId: 'default', + validate: { + params: schema.object({ + param1: schema.string(), + }), + }, + async executor({ services, params, state }: AlertExecutorOptions) {}, + }; + const noopAlertType: AlertType = { + id: 'test.noop', + name: 'Test: Noop', + actionGroups: [{ id: 'default', name: 'Default' }], + defaultActionGroupId: 'default', + async executor({ services, params, state }: AlertExecutorOptions) {}, + }; + const onlyContextVariablesAlertType: AlertType = { + id: 'test.onlyContextVariables', + name: 'Test: Only Context Variables', + actionGroups: [{ id: 'default', name: 'Default' }], + defaultActionGroupId: 'default', + actionVariables: { + context: [{ name: 'aContextVariable', description: 'this is a context variable' }], + }, + async executor(opts: AlertExecutorOptions) {}, + }; + const onlyStateVariablesAlertType: AlertType = { + id: 'test.onlyStateVariables', + name: 'Test: Only State Variables', + actionGroups: [{ id: 'default', name: 'Default' }], + defaultActionGroupId: 'default', + actionVariables: { + state: [{ name: 'aStateVariable', description: 'this is a state variable' }], + }, + async executor(opts: AlertExecutorOptions) {}, + }; + alerting.registerType(alwaysFiringAlertType); + alerting.registerType(cumulativeFiringAlertType); + alerting.registerType(neverFiringAlertType); + alerting.registerType(failingAlertType); + alerting.registerType(validationAlertType); + alerting.registerType(authorizationAlertType); + alerting.registerType(noopAlertType); + alerting.registerType(onlyContextVariablesAlertType); + alerting.registerType(onlyStateVariablesAlertType); + } + + public start() {} + public stop() {} +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager/index.ts deleted file mode 100644 index ac32f05805e4a3..00000000000000 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager/index.ts +++ /dev/null @@ -1,60 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { TaskManagerStartContract } from '../../../../../../plugins/task_manager/server'; - -const taskManagerQuery = (...filters: any[]) => ({ - bool: { - filter: { - bool: { - must: filters, - }, - }, - }, -}); - -const tasksForAlerting = { - term: { - 'task.scope': 'alerting', - }, -}; -const taskByIdQuery = (id: string) => ({ - ids: { - values: [`task:${id}`], - }, -}); - -// eslint-disable-next-line import/no-default-export -export default function(kibana: any) { - return new kibana.Plugin({ - name: 'taskManagerHelpers', - require: ['elasticsearch'], - - config(Joi: any) { - return Joi.object({ - enabled: Joi.boolean().default(true), - }).default(); - }, - - init(server: any) { - const taskManager = server.newPlatform.start.plugins.taskManager as TaskManagerStartContract; - - server.route({ - path: '/api/alerting_tasks/{taskId}', - method: 'GET', - async handler(request: any) { - try { - return taskManager.fetch({ - query: taskManagerQuery(tasksForAlerting, taskByIdQuery(request.params.taskId)), - }); - } catch (err) { - return err; - } - }, - }); - }, - }); -} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager/package.json b/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager/package.json deleted file mode 100644 index 532b597829561c..00000000000000 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "alerting_task_plugin", - "version": "1.0.0", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0", - "dependencies": { - "joi": "^13.5.2" - } -} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/kibana.json b/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/kibana.json new file mode 100644 index 00000000000000..8f606276998f58 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "task_manager_fixture", + "version": "1.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack"], + "requiredPlugins": ["taskManager"], + "server": true, + "ui": false +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/package.json b/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/package.json new file mode 100644 index 00000000000000..be3b542ed7b52c --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/package.json @@ -0,0 +1,20 @@ +{ + "name": "task_manager_fixture", + "version": "1.0.0", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "main": "target/test/plugin_api_integration/plugins/task_manager_fixture", + "scripts": { + "kbn": "node ../../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "3.7.2" + }, + "license": "Apache-2.0", + "dependencies": { + "joi": "^13.5.2" + } +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/server/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/server/index.ts new file mode 100644 index 00000000000000..77233f463734a1 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/server/index.ts @@ -0,0 +1,9 @@ +/* + * 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 { SampleTaskManagerFixturePlugin } from './plugin'; + +export const plugin = () => new SampleTaskManagerFixturePlugin(); diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/server/plugin.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/server/plugin.ts new file mode 100644 index 00000000000000..18fdd5f9c3ac37 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager_fixture/server/plugin.ts @@ -0,0 +1,87 @@ +/* + * 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 { + Plugin, + CoreSetup, + CoreStart, + RequestHandlerContext, + KibanaRequest, + KibanaResponseFactory, + IKibanaResponse, +} from 'kibana/server'; +import { Subject } from 'rxjs'; +import { first } from 'rxjs/operators'; +import { schema } from '@kbn/config-schema'; +import { TaskManagerStartContract } from '../../../../../../../plugins/task_manager/server'; + +export interface SampleTaskManagerFixtureStartDeps { + taskManager: TaskManagerStartContract; +} + +const taskManagerQuery = (...filters: any[]) => ({ + bool: { + filter: { + bool: { + must: filters, + }, + }, + }, +}); + +const tasksForAlerting = { + term: { + 'task.scope': 'alerting', + }, +}; +const taskByIdQuery = (id: string) => ({ + ids: { + values: [`task:${id}`], + }, +}); + +export class SampleTaskManagerFixturePlugin + implements Plugin { + taskManagerStart$: Subject = new Subject(); + taskManagerStart: Promise = this.taskManagerStart$ + .pipe(first()) + .toPromise(); + + public setup(core: CoreSetup) { + core.http.createRouter().get( + { + path: '/api/alerting_tasks/{taskId}', + validate: { + params: schema.object({ + taskId: schema.string(), + }), + }, + }, + async ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> => { + try { + const taskManager = await this.taskManagerStart; + return res.ok({ + body: await taskManager.fetch({ + query: taskManagerQuery(tasksForAlerting, taskByIdQuery(req.params.taskId)), + }), + }); + } catch (err) { + return res.badRequest({ body: err }); + } + } + ); + } + + public start(core: CoreStart, { taskManager }: SampleTaskManagerFixtureStartDeps) { + this.taskManagerStart$.next(taskManager); + this.taskManagerStart$.complete(); + } + public stop() {} +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts index ed63d25d86aca6..c67f7868f7ff0f 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators'; +} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; const mapping = [ { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts index 4c76ebfb93b0bd..443d3acb7ba1c7 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators'; +} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function pagerdutyTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts index 04cd06999f4321..c69eead75e2787 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators'; +} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; const mapping = [ { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts index 386254e49c19c8..e522b8c7b898dc 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators'; +} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function slackTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts index 9b66326fa6157a..9c769897d35eee 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts @@ -10,7 +10,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators'; +} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; const defaultValues: Record = { headers: null, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/webhook.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/webhook.ts index 112149a32649a2..22e1284f55aa87 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/webhook.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/webhook.ts @@ -10,7 +10,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions_simulators'; +} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; // eslint-disable-next-line import/no-default-export export default function webhookTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/plugin_api_perf/config.js b/x-pack/test/plugin_api_perf/config.js index 9a7d0071afcc4a..9c6d8873b8c844 100644 --- a/x-pack/test/plugin_api_perf/config.js +++ b/x-pack/test/plugin_api_perf/config.js @@ -39,7 +39,7 @@ export default async function({ readConfigFile }) { '..', 'plugin_api_integration', 'plugins', - 'task_manager' + 'sample_task_plugin' )}`, ...plugins.map( pluginDir => `--plugin-path=${path.resolve(__dirname, 'plugins', pluginDir)}` diff --git a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/index.js b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/index.js deleted file mode 100644 index 87e3b3b66a2019..00000000000000 --- a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/index.js +++ /dev/null @@ -1,378 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import uuid from 'uuid'; -import _ from 'lodash'; -import stats from 'stats-lite'; -import prettyMilliseconds from 'pretty-ms'; -import { performance, PerformanceObserver } from 'perf_hooks'; -import { initRoutes } from './init_routes'; - -export default function TaskManagerPerformanceAPI(kibana) { - return new kibana.Plugin({ - name: 'perfTask', - require: ['elasticsearch', 'task_manager'], - - config(Joi) { - return Joi.object({ - enabled: Joi.boolean().default(true), - }).default(); - }, - - init(server) { - const taskManager = { - ...server.newPlatform.setup.plugins.taskManager, - ...server.newPlatform.start.plugins.taskManager, - }; - const performanceState = resetPerfState({}); - - let lastFlush = new Date(); - function flushPerfStats() { - setTimeout(flushPerfStats, 5000); - const prevFlush = lastFlush; - lastFlush = new Date(); - - const tasks = performanceState.leadTimeQueue.length; - const title = `[Perf${performanceState.capturing ? ' (capturing)' : ''}]`; - const seconds = parseInt((lastFlush - prevFlush) / 1000); - console.log( - `${title} I have processed ${tasks} tasks in the past ${seconds}s (${tasks / - seconds} per second)` - ); - if (tasks > 0) { - const latestAverage = avg(performanceState.leadTimeQueue.splice(0, tasks)).mean; - - performanceState.averagesTakenLeadTime.push(latestAverage); - performanceState.averagesTaken.push(tasks); - if (performanceState.averagesTakenLeadTime.length > 1) { - performanceState.runningAverageLeadTime = avg( - performanceState.averagesTakenLeadTime - ).mean; - performanceState.runningAverageTasksPerSecond = - avg(performanceState.averagesTaken).mean / 5; - } else { - performanceState.runningAverageLeadTime = latestAverage; - performanceState.runningAverageTasksPerSecond = tasks / 5; - } - } - } - - setTimeout(flushPerfStats, 5000); - - const title = 'Perf Test Task'; - - taskManager.registerTaskDefinitions({ - performanceTestTask: { - title, - description: 'A task for stress testing task_manager.', - timeout: '1m', - - createTaskRunner: ({ taskInstance }) => { - return { - async run() { - const { params, state } = taskInstance; - - const counter = state.counter ? state.counter : 1; - - const now = Date.now(); - const leadTime = now - taskInstance.runAt; - performanceState.leadTimeQueue.push(leadTime); - - // schedule to run next cycle as soon as possible - const runAt = calRunAt(params, counter); - - const stateUpdated = { - ...state, - counter: counter + 1, - }; - - if (params.trackExecutionTimeline && state.perf && state.perf.id) { - performance.mark(`perfTask_run_${state.perf.id}_${counter}`); - performance.measure( - 'perfTask.markUntilRun', - `perfTask_markAsRunning_${state.perf.id}_${counter}`, - `perfTask_run_${state.perf.id}_${counter}` - ); - if (counter === 1) { - performance.measure( - 'perfTask.firstRun', - `perfTask_schedule_${state.perf.id}`, - `perfTask_run_${state.perf.id}_${counter}` - ); - performance.measure( - 'perfTask.firstMarkAsRunningTillRan', - `perfTask_markAsRunning_${state.perf.id}_${counter}`, - `perfTask_run_${state.perf.id}_${counter}` - ); - } - } - - return { - state: stateUpdated, - runAt, - }; - }, - }; - }, - }, - }); - - taskManager.addMiddleware({ - async beforeSave({ taskInstance, ...opts }) { - const modifiedInstance = { - ...taskInstance, - }; - - if (taskInstance.params && taskInstance.params.trackExecutionTimeline) { - modifiedInstance.state = modifiedInstance.state || {}; - modifiedInstance.state.perf = modifiedInstance.state.perf || {}; - modifiedInstance.state.perf.id = uuid.v4().replace(/-/gi, '_'); - performance.mark(`perfTask_schedule_${modifiedInstance.state.perf.id}`); - } - - return { - ...opts, - taskInstance: modifiedInstance, - }; - }, - - async beforeMarkRunning({ taskInstance, ...opts }) { - const modifiedInstance = { - ...taskInstance, - }; - - if ( - modifiedInstance.state && - modifiedInstance.state.perf && - modifiedInstance.state.perf.id - ) { - const { counter = 1 } = modifiedInstance.state; - performance.mark(`perfTask_markAsRunning_${modifiedInstance.state.perf.id}_${counter}`); - if (counter === 1) { - performance.measure( - 'perfTask.firstMarkAsRunning', - `perfTask_schedule_${modifiedInstance.state.perf.id}`, - `perfTask_markAsRunning_${modifiedInstance.state.perf.id}_${counter}` - ); - } else if (counter > 1) { - performance.measure( - 'perfTask.runUntilNextMarkAsRunning', - `perfTask_run_${modifiedInstance.state.perf.id}_${counter - 1}`, - `perfTask_markAsRunning_${modifiedInstance.state.perf.id}_${counter}` - ); - } - } - - return { - ...opts, - taskInstance: modifiedInstance, - }; - }, - }); - - const perfApi = { - capture() { - resetPerfState(performanceState); - performanceState.capturing = true; - performance.mark('perfTest.start'); - }, - endCapture() { - return new Promise(resolve => { - performanceState.performance.summarize.push([resolve, perfApi.summarize]); - - performance.mark('perfTest.end'); - performance.measure('perfTest.duration', 'perfTest.start', 'perfTest.end'); - }); - }, - summarize(perfTestDuration) { - const { - runningAverageTasksPerSecond, - runningAverageLeadTime, - performance, - } = performanceState; - - const { - numberOfTasksRanOverall, - elasticsearchApiCalls, - activityDuration, - sleepDuration, - cycles, - claimAvailableTasksNoTasks, - claimAvailableTasksNoAvailableWorkers, - taskPoolAttemptToRun, - taskRunnerMarkTaskAsRunning, - } = performance; - - const perfRes = { - perfTestDuration: prettyMilliseconds(perfTestDuration), - runningAverageTasksPerSecond, - runningAverageLeadTime, - numberOfTasksRanOverall, - claimAvailableTasksNoTasks, - claimAvailableTasksNoAvailableWorkers, - elasticsearchApiCalls: _.mapValues(elasticsearchApiCalls, avg), - sleepDuration: prettyMilliseconds(stats.sum(sleepDuration)), - activityDuration: prettyMilliseconds(stats.sum(activityDuration)), - cycles, - taskPoolAttemptToRun: avg(taskPoolAttemptToRun), - taskRunnerMarkTaskAsRunning: avg(taskRunnerMarkTaskAsRunning), - }; - - resetPerfState(performanceState); - - return perfRes; - }, - }; - - initRoutes(server, perfApi); - }, - }); -} - -function calRunAt(params, counter) { - const runAt = counter === 1 ? new Date(params.startAt) : new Date(); - return runAt.getTime() < params.runUntil ? runAt : undefined; -} - -function avg(items) { - const mode = stats.mode(items); - return { - mean: parseInt(stats.mean(items)), - range: { - min: parseInt(typeof mode === 'number' ? mode : _.min([...mode])), - max: parseInt(typeof mode === 'number' ? mode : _.max([...mode])), - }, - }; -} - -function resetPerfState(target) { - if (target.performanceObserver) { - target.performanceObserver.disconnect(); - } - - const performanceState = Object.assign(target, { - capturing: false, - runningAverageTasksPerSecond: 0, - averagesTaken: [], - runningAverageLeadTime: -1, - averagesTakenLeadTime: [], - leadTimeQueue: [], - performance: { - numberOfTasksRanOverall: 0, - cycles: { - fillPoolStarts: 0, - fillPoolCycles: 0, - fillPoolBail: 0, - claimedOnRerunCycle: 0, - fillPoolBailNoTasks: 0, - }, - claimAvailableTasksNoTasks: 0, - claimAvailableTasksNoAvailableWorkers: 0, - elasticsearchApiCalls: { - timeUntilFirstRun: [], - timeUntilFirstMarkAsRun: [], - firstMarkAsRunningTillRan: [], - timeFromMarkAsRunTillRun: [], - timeFromRunTillNextMarkAsRun: [], - claimAvailableTasks: [], - }, - activityDuration: [], - sleepDuration: [], - taskPollerActivityDurationPreScheduleComplete: [], - taskPoolAttemptToRun: [], - taskRunnerMarkTaskAsRunning: [], - - summarize: [], - }, - }); - - performanceState.performanceObserver = new PerformanceObserver((list, observer) => { - list.getEntries().forEach(entry => { - const { name, duration } = entry; - switch (name) { - // Elasticsearch Api Calls - case 'perfTask.firstRun': - performanceState.performance.elasticsearchApiCalls.timeUntilFirstRun.push(duration); - break; - case 'perfTask.firstMarkAsRunning': - performanceState.performance.elasticsearchApiCalls.timeUntilFirstMarkAsRun.push(duration); - break; - case 'perfTask.firstMarkAsRunningTillRan': - performanceState.performance.elasticsearchApiCalls.firstMarkAsRunningTillRan.push( - duration - ); - break; - case 'perfTask.markUntilRun': - performanceState.performance.elasticsearchApiCalls.timeFromMarkAsRunTillRun.push( - duration - ); - break; - case 'perfTask.runUntilNextMarkAsRunning': - performanceState.performance.elasticsearchApiCalls.timeFromRunTillNextMarkAsRun.push( - duration - ); - break; - case 'claimAvailableTasks': - performanceState.performance.elasticsearchApiCalls.claimAvailableTasks.push(duration); - break; - case 'TaskPoller.sleepDuration': - performanceState.performance.sleepDuration.push(duration); - break; - case 'fillPool.activityDurationUntilNoTasks': - performanceState.performance.activityDuration.push(duration); - break; - case 'fillPool.activityDurationUntilExhaustedCapacity': - performanceState.performance.activityDuration.push(duration); - break; - case 'fillPool.bailExhaustedCapacity': - performanceState.performance.cycles.fillPoolBail++; - break; - case 'fillPool.claimedOnRerunCycle': - performanceState.performance.cycles.claimedOnRerunCycle++; - break; - case 'fillPool.bailNoTasks': - performanceState.performance.cycles.fillPoolBail++; - performanceState.performance.cycles.fillPoolBailNoTasks++; - break; - case 'fillPool.start': - performanceState.performance.cycles.fillPoolStarts++; - break; - case 'fillPool.cycle': - performanceState.performance.cycles.fillPoolCycles++; - break; - break; - case 'claimAvailableTasks.noTasks': - performanceState.performance.claimAvailableTasksNoTasks++; - break; - case 'claimAvailableTasks.noAvailableWorkers': - performanceState.performance.claimAvailableTasksNoAvailableWorkers++; - break; - case 'taskPool.attemptToRun': - performanceState.performance.taskPoolAttemptToRun.push(duration); - break; - case 'taskRunner.markTaskAsRunning': - performanceState.performance.taskRunnerMarkTaskAsRunning.push(duration); - break; - case 'perfTest.duration': - observer.disconnect(); - const { summarize } = performanceState.performance; - if (summarize && summarize.length) { - summarize.splice(0, summarize.length).forEach(([resolve, summarize]) => { - resolve(summarize(duration)); - }); - } - break; - default: - if (name.startsWith('perfTask_run_')) { - performanceState.performance.numberOfTasksRanOverall++; - } - } - }); - }); - performanceState.performanceObserver.observe({ entryTypes: ['measure', 'mark'] }); - - return performanceState; -} diff --git a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/init_routes.js b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/init_routes.js deleted file mode 100644 index 6cd706a6ebecdf..00000000000000 --- a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/init_routes.js +++ /dev/null @@ -1,77 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import Joi from 'joi'; -import { range, chunk } from 'lodash'; - -const scope = 'perf-testing'; -export function initRoutes(server, performanceState) { - const taskManager = { - ...server.newPlatform.setup.plugins.taskManager, - ...server.newPlatform.start.plugins.taskManager, - }; - - server.route({ - path: '/api/perf_tasks', - method: 'POST', - config: { - validate: { - payload: Joi.object({ - tasksToSpawn: Joi.number().required(), - durationInSeconds: Joi.number().required(), - trackExecutionTimeline: Joi.boolean() - .default(false) - .required(), - }), - }, - }, - async handler(request) { - performanceState.capture(); - - const { tasksToSpawn, durationInSeconds, trackExecutionTimeline } = request.payload; - const startAt = millisecondsFromNow(5000).getTime(); - await chunk(range(tasksToSpawn), 200) - .map(chunkOfTasksToSpawn => () => - Promise.all( - chunkOfTasksToSpawn.map(taskIndex => - taskManager.schedule( - { - taskType: 'performanceTestTask', - params: { - startAt, - taskIndex, - trackExecutionTimeline, - runUntil: millisecondsFromNow(durationInSeconds * 1000).getTime(), - }, - scope: [scope], - }, - { request } - ) - ) - ) - ) - .reduce((chain, nextExecutor) => { - return chain.then(() => nextExecutor()); - }, Promise.resolve()); - - return new Promise(resolve => { - setTimeout(() => { - performanceState.endCapture().then(resolve); - }, durationInSeconds * 1000 + 10000 /* wait extra 10s to drain queue */); - }); - }, - }); -} - -function millisecondsFromNow(ms) { - if (!ms) { - return; - } - - const dt = new Date(); - dt.setTime(dt.getTime() + ms); - return dt; -} diff --git a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/kibana.json b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/kibana.json new file mode 100644 index 00000000000000..1fa480cd53c483 --- /dev/null +++ b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "task_manager_performance", + "version": "1.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack"], + "requiredPlugins": ["taskManager"], + "server": true, + "ui": false +} diff --git a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/package.json b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/package.json index 7d46d6b0f3cca6..9cb3859271d812 100644 --- a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/package.json +++ b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/package.json @@ -1,16 +1,23 @@ { - "name": "perf_task_plugin", + "name": "task_manager_performance", "version": "1.0.0", "kibana": { "version": "kibana", "templateVersion": "1.0.0" }, - "license": "Apache-2.0", + "main": "target/test/plugin_api_perf/plugins/task_manager_performance", + "scripts": { + "kbn": "node ../../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, "dependencies": { "lodash": "^4.17.15", "uuid": "3.3.2", - "joi": "^13.5.2", "stats-lite": "2.2.0", "pretty-ms": "5.0.0" - } + }, + "devDependencies": { + "typescript": "3.7.2" + }, + "license": "Apache-2.0" } diff --git a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/index.ts b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/index.ts new file mode 100644 index 00000000000000..77233f463734a1 --- /dev/null +++ b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/index.ts @@ -0,0 +1,9 @@ +/* + * 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 { SampleTaskManagerFixturePlugin } from './plugin'; + +export const plugin = () => new SampleTaskManagerFixturePlugin(); diff --git a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/init_routes.ts b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/init_routes.ts new file mode 100644 index 00000000000000..947cc0743e2f8d --- /dev/null +++ b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/init_routes.ts @@ -0,0 +1,92 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { + RequestHandlerContext, + KibanaRequest, + KibanaResponseFactory, + IKibanaResponse, + IRouter, + CoreSetup, +} from 'kibana/server'; +import { range, chunk } from 'lodash'; +import { + TaskManagerStartContract, + ConcreteTaskInstance, +} from '../../../../../plugins/task_manager/server'; +import { PerfApi, PerfResult } from './types'; + +const scope = 'perf-testing'; + +export function initRoutes( + router: IRouter, + core: CoreSetup, + taskManagerStart: Promise, + performanceApi: PerfApi +) { + router.post( + { + path: '/api/perf_tasks', + validate: { + body: schema.object({ + tasksToSpawn: schema.number(), + durationInSeconds: schema.number(), + trackExecutionTimeline: schema.boolean({ defaultValue: false }), + }), + }, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + performanceApi.capture(); + + const taskManager = await taskManagerStart; + + const { tasksToSpawn, durationInSeconds, trackExecutionTimeline } = req.body; + const startAt = millisecondsFromNow(5000).getTime(); + await chunk(range(tasksToSpawn), 200) + .map(chunkOfTasksToSpawn => () => + Promise.all( + chunkOfTasksToSpawn.map(async taskIndex => + taskManager.schedule( + { + taskType: 'performanceTestTask', + params: { + startAt, + taskIndex, + trackExecutionTimeline, + runUntil: millisecondsFromNow(durationInSeconds * 1000).getTime(), + }, + state: {}, + scope: [scope], + }, + { request: req } + ) + ) + ) + ) + .reduce((chain, nextExecutor) => { + return chain.then(() => nextExecutor()); + }, Promise.resolve(undefined)); + + return res.ok({ + body: await new Promise(resolve => { + setTimeout(() => { + performanceApi.endCapture().then((perf: PerfResult) => resolve(perf)); + }, durationInSeconds * 1000 + 10000 /* wait extra 10s to drain queue */); + }), + }); + } + ); +} + +function millisecondsFromNow(ms: number): Date { + const dt = new Date(); + dt.setTime(dt.getTime() + ms); + return dt; +} diff --git a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/plugin.ts b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/plugin.ts new file mode 100644 index 00000000000000..dad17b32b33f40 --- /dev/null +++ b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/plugin.ts @@ -0,0 +1,404 @@ +/* + * 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 { Plugin, CoreSetup, CoreStart } from 'kibana/server'; +import { Subject } from 'rxjs'; +import { first } from 'rxjs/operators'; + +import uuid from 'uuid'; +import _ from 'lodash'; +import stats from 'stats-lite'; +import prettyMilliseconds from 'pretty-ms'; +import { performance, PerformanceObserver } from 'perf_hooks'; +import { + TaskManagerSetupContract, + TaskManagerStartContract, + ConcreteTaskInstance, +} from '../../../../../plugins/task_manager/server'; +import { PerfState, PerfApi, PerfResult } from './types'; +import { initRoutes } from './init_routes'; + +// this plugin's dependendencies +export interface SampleTaskManagerFixtureSetupDeps { + taskManager: TaskManagerSetupContract; +} +export interface SampleTaskManagerFixtureStartDeps { + taskManager: TaskManagerStartContract; +} + +export class SampleTaskManagerFixturePlugin + implements + Plugin { + taskManagerStart$: Subject = new Subject(); + taskManagerStart: Promise = this.taskManagerStart$ + .pipe(first()) + .toPromise(); + + public setup(core: CoreSetup, { taskManager }: SampleTaskManagerFixtureSetupDeps) { + const performanceState = resetPerfState({}); + + let lastFlush = new Date(); + function flushPerfStats() { + setTimeout(flushPerfStats, 5000); + const prevFlush = lastFlush; + lastFlush = new Date(); + + const tasks = performanceState.leadTimeQueue.length; + const title = `[Perf${performanceState.capturing ? ' (capturing)' : ''}]`; + const seconds = Math.round((lastFlush.getTime() - prevFlush.getTime()) / 1000); + // eslint-disable-next-line no-console + console.log( + `${title} I have processed ${tasks} tasks in the past ${seconds}s (${tasks / + seconds} per second)` + ); + if (tasks > 0) { + const latestAverage = avg(performanceState.leadTimeQueue.splice(0, tasks)).mean; + + performanceState.averagesTakenLeadTime.push(latestAverage); + performanceState.averagesTaken.push(tasks); + if (performanceState.averagesTakenLeadTime.length > 1) { + performanceState.runningAverageLeadTime = avg( + performanceState.averagesTakenLeadTime + ).mean; + performanceState.runningAverageTasksPerSecond = + avg(performanceState.averagesTaken).mean / 5; + } else { + performanceState.runningAverageLeadTime = latestAverage; + performanceState.runningAverageTasksPerSecond = tasks / 5; + } + } + } + + setTimeout(flushPerfStats, 5000); + + const title = 'Perf Test Task'; + + taskManager.registerTaskDefinitions({ + performanceTestTask: { + type: 'performanceTestTask', + title, + description: 'A task for stress testing task_manager.', + timeout: '1m', + + createTaskRunner: ({ taskInstance }: { taskInstance: ConcreteTaskInstance }) => { + return { + async run() { + const { params, state } = taskInstance; + + const counter = state.counter ? state.counter : 1; + + const now = Date.now(); + const leadTime = now - taskInstance.runAt.getTime(); + performanceState.leadTimeQueue.push(leadTime); + + // schedule to run next cycle as soon as possible + const runAt = calRunAt(params, counter); + + const stateUpdated = { + ...state, + counter: counter + 1, + }; + + if (params.trackExecutionTimeline && state.perf && state.perf.id) { + performance.mark(`perfTask_run_${state.perf.id}_${counter}`); + performance.measure( + 'perfTask.markUntilRun', + `perfTask_markAsRunning_${state.perf.id}_${counter}`, + `perfTask_run_${state.perf.id}_${counter}` + ); + if (counter === 1) { + performance.measure( + 'perfTask.firstRun', + `perfTask_schedule_${state.perf.id}`, + `perfTask_run_${state.perf.id}_${counter}` + ); + performance.measure( + 'perfTask.firstMarkAsRunningTillRan', + `perfTask_markAsRunning_${state.perf.id}_${counter}`, + `perfTask_run_${state.perf.id}_${counter}` + ); + } + } + + return { + state: stateUpdated, + runAt, + }; + }, + }; + }, + }, + }); + + taskManager.addMiddleware({ + async beforeSave({ taskInstance, ...opts }) { + const modifiedInstance = { + ...taskInstance, + }; + + if (taskInstance.params && taskInstance.params.trackExecutionTimeline) { + modifiedInstance.state = modifiedInstance.state || {}; + modifiedInstance.state.perf = modifiedInstance.state.perf || {}; + modifiedInstance.state.perf.id = uuid.v4().replace(/-/gi, '_'); + performance.mark(`perfTask_schedule_${modifiedInstance.state.perf.id}`); + } + + return { + ...opts, + taskInstance: modifiedInstance, + }; + }, + + async beforeRun(opts) { + return opts; + }, + + async beforeMarkRunning({ taskInstance, ...opts }) { + const modifiedInstance = { + ...taskInstance, + }; + + if ( + modifiedInstance.state && + modifiedInstance.state.perf && + modifiedInstance.state.perf.id + ) { + const { counter = 1 } = modifiedInstance.state; + performance.mark(`perfTask_markAsRunning_${modifiedInstance.state.perf.id}_${counter}`); + if (counter === 1) { + performance.measure( + 'perfTask.firstMarkAsRunning', + `perfTask_schedule_${modifiedInstance.state.perf.id}`, + `perfTask_markAsRunning_${modifiedInstance.state.perf.id}_${counter}` + ); + } else if (counter > 1) { + performance.measure( + 'perfTask.runUntilNextMarkAsRunning', + `perfTask_run_${modifiedInstance.state.perf.id}_${counter - 1}`, + `perfTask_markAsRunning_${modifiedInstance.state.perf.id}_${counter}` + ); + } + } + + return { + ...opts, + taskInstance: modifiedInstance, + }; + }, + }); + + const perfApi: PerfApi = { + capture() { + resetPerfState(performanceState); + performanceState.capturing = true; + performance.mark('perfTest.start'); + }, + endCapture() { + return new Promise(resolve => { + performanceState.performance.summarize.push([resolve, perfApi.summarize]); + + performance.mark('perfTest.end'); + performance.measure('perfTest.duration', 'perfTest.start', 'perfTest.end'); + }); + }, + summarize(perfTestDuration: number) { + const { + runningAverageTasksPerSecond, + runningAverageLeadTime, + performance: { + numberOfTasksRanOverall, + elasticsearchApiCalls, + activityDuration, + sleepDuration, + cycles, + claimAvailableTasksNoTasks, + claimAvailableTasksNoAvailableWorkers, + taskPoolAttemptToRun, + taskRunnerMarkTaskAsRunning, + }, + } = performanceState; + + const perfRes: PerfResult = { + perfTestDuration: prettyMilliseconds(perfTestDuration), + runningAverageTasksPerSecond, + runningAverageLeadTime, + numberOfTasksRanOverall, + claimAvailableTasksNoTasks, + claimAvailableTasksNoAvailableWorkers, + elasticsearchApiCalls: (_.mapValues( + elasticsearchApiCalls, + avg + ) as unknown) as PerfResult['elasticsearchApiCalls'], + sleepDuration: prettyMilliseconds(stats.sum(sleepDuration)), + activityDuration: prettyMilliseconds(stats.sum(activityDuration)), + cycles, + taskPoolAttemptToRun: avg(taskPoolAttemptToRun), + taskRunnerMarkTaskAsRunning: avg(taskRunnerMarkTaskAsRunning), + }; + + resetPerfState(performanceState); + + return perfRes; + }, + }; + initRoutes(core.http.createRouter(), core, this.taskManagerStart, perfApi); + } + + public start(core: CoreStart, { taskManager }: SampleTaskManagerFixtureStartDeps) { + this.taskManagerStart$.next(taskManager); + this.taskManagerStart$.complete(); + } + public stop() {} +} + +function calRunAt(params: ConcreteTaskInstance['params'], counter: number) { + const runAt = counter === 1 ? new Date(params.startAt) : new Date(); + return runAt.getTime() < params.runUntil ? runAt : undefined; +} + +function avg(items: number[]) { + const mode = stats.mode(items); + return { + mean: Math.round(stats.mean(items)), + range: { + min: Math.round(isNumericArray(mode) ? _.min([...mode]) : mode), + max: Math.round(isNumericArray(mode) ? _.max([...mode]) : mode), + }, + }; +} + +function isNumericArray(mode: unknown): mode is number[] { + return Array.isArray(mode); +} + +function resetPerfState(target: Partial): PerfState { + if (target.performanceObserver) { + target.performanceObserver.disconnect(); + } + + const performanceState = Object.assign(target, { + capturing: false, + runningAverageTasksPerSecond: 0, + averagesTaken: [], + runningAverageLeadTime: -1, + averagesTakenLeadTime: [], + leadTimeQueue: [], + performance: { + numberOfTasksRanOverall: 0, + cycles: { + fillPoolStarts: 0, + fillPoolCycles: 0, + fillPoolBail: 0, + claimedOnRerunCycle: 0, + fillPoolBailNoTasks: 0, + }, + claimAvailableTasksNoTasks: 0, + claimAvailableTasksNoAvailableWorkers: 0, + elasticsearchApiCalls: { + timeUntilFirstRun: [], + timeUntilFirstMarkAsRun: [], + firstMarkAsRunningTillRan: [], + timeFromMarkAsRunTillRun: [], + timeFromRunTillNextMarkAsRun: [], + claimAvailableTasks: [], + }, + activityDuration: [], + sleepDuration: [], + taskPollerActivityDurationPreScheduleComplete: [], + taskPoolAttemptToRun: [], + taskRunnerMarkTaskAsRunning: [], + + summarize: [], + }, + }); + + performanceState.performanceObserver = new PerformanceObserver((list, observer) => { + list.getEntries().forEach(entry => { + const { name, duration } = entry; + switch (name) { + // Elasticsearch Api Calls + case 'perfTask.firstRun': + performanceState.performance.elasticsearchApiCalls.timeUntilFirstRun.push(duration); + break; + case 'perfTask.firstMarkAsRunning': + performanceState.performance.elasticsearchApiCalls.timeUntilFirstMarkAsRun.push(duration); + break; + case 'perfTask.firstMarkAsRunningTillRan': + performanceState.performance.elasticsearchApiCalls.firstMarkAsRunningTillRan.push( + duration + ); + break; + case 'perfTask.markUntilRun': + performanceState.performance.elasticsearchApiCalls.timeFromMarkAsRunTillRun.push( + duration + ); + break; + case 'perfTask.runUntilNextMarkAsRunning': + performanceState.performance.elasticsearchApiCalls.timeFromRunTillNextMarkAsRun.push( + duration + ); + break; + case 'claimAvailableTasks': + performanceState.performance.elasticsearchApiCalls.claimAvailableTasks.push(duration); + break; + case 'TaskPoller.sleepDuration': + performanceState.performance.sleepDuration.push(duration); + break; + case 'fillPool.activityDurationUntilNoTasks': + performanceState.performance.activityDuration.push(duration); + break; + case 'fillPool.activityDurationUntilExhaustedCapacity': + performanceState.performance.activityDuration.push(duration); + break; + case 'fillPool.bailExhaustedCapacity': + performanceState.performance.cycles.fillPoolBail++; + break; + case 'fillPool.claimedOnRerunCycle': + performanceState.performance.cycles.claimedOnRerunCycle++; + break; + case 'fillPool.bailNoTasks': + performanceState.performance.cycles.fillPoolBail++; + performanceState.performance.cycles.fillPoolBailNoTasks++; + break; + case 'fillPool.start': + performanceState.performance.cycles.fillPoolStarts++; + break; + case 'fillPool.cycle': + performanceState.performance.cycles.fillPoolCycles++; + break; + break; + case 'claimAvailableTasks.noTasks': + performanceState.performance.claimAvailableTasksNoTasks++; + break; + case 'claimAvailableTasks.noAvailableWorkers': + performanceState.performance.claimAvailableTasksNoAvailableWorkers++; + break; + case 'taskPool.attemptToRun': + performanceState.performance.taskPoolAttemptToRun.push(duration); + break; + case 'taskRunner.markTaskAsRunning': + performanceState.performance.taskRunnerMarkTaskAsRunning.push(duration); + break; + case 'perfTest.duration': + observer.disconnect(); + const { summarize } = performanceState.performance; + if (summarize && summarize.length) { + summarize.splice(0, summarize.length).forEach(([resolve, callSummarize]) => { + resolve(callSummarize(duration)); + }); + } + break; + default: + if (name.startsWith('perfTask_run_')) { + performanceState.performance.numberOfTasksRanOverall++; + } + } + }); + }); + performanceState.performanceObserver.observe({ entryTypes: ['measure', 'mark'] }); + + return performanceState; +} diff --git a/x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/types.ts b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/types.ts new file mode 100644 index 00000000000000..ac5e8cdaba9369 --- /dev/null +++ b/x-pack/test/plugin_api_perf/plugins/task_manager_performance/server/types.ts @@ -0,0 +1,82 @@ +/* + * 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 { PerformanceObserver } from 'perf_hooks'; + +export interface Perf { + numberOfTasksRanOverall: number; + cycles: { + fillPoolStarts: number; + fillPoolCycles: number; + fillPoolBail: number; + claimedOnRerunCycle: number; + fillPoolBailNoTasks: number; + }; + claimAvailableTasksNoTasks: number; + claimAvailableTasksNoAvailableWorkers: number; + elasticsearchApiCalls: { + timeUntilFirstRun: number[]; + timeUntilFirstMarkAsRun: number[]; + firstMarkAsRunningTillRan: number[]; + timeFromMarkAsRunTillRun: number[]; + timeFromRunTillNextMarkAsRun: number[]; + claimAvailableTasks: number[]; + }; + activityDuration: number[]; + sleepDuration: number[]; + taskPollerActivityDurationPreScheduleComplete: number[]; + taskPoolAttemptToRun: number[]; + taskRunnerMarkTaskAsRunning: number[]; + + summarize: Array<[(perf: PerfResult) => void, (perfTestDuration: number) => PerfResult]>; +} + +export interface PerfState { + performanceObserver?: PerformanceObserver; + runningAverageTasksPerSecond: number; + averagesTaken: number[]; + runningAverageLeadTime: number; + averagesTakenLeadTime: number[]; + leadTimeQueue: number[]; + performance: Perf; + capturing: boolean; +} + +export interface PerfResult { + perfTestDuration: string; + runningAverageTasksPerSecond: number; + runningAverageLeadTime: number; + numberOfTasksRanOverall: number; + claimAvailableTasksNoTasks: number; + claimAvailableTasksNoAvailableWorkers: number; + elasticsearchApiCalls: { + timeUntilFirstRun: PerfAvg; + timeUntilFirstMarkAsRun: PerfAvg; + firstMarkAsRunningTillRan: PerfAvg; + timeFromMarkAsRunTillRun: PerfAvg; + timeFromRunTillNextMarkAsRun: PerfAvg; + claimAvailableTasks: PerfAvg; + }; + sleepDuration: string; + activityDuration: string; + cycles: Perf['cycles']; + taskPoolAttemptToRun: PerfAvg; + taskRunnerMarkTaskAsRunning: PerfAvg; +} + +export interface PerfApi { + capture: () => void; + endCapture: () => Promise; + summarize: (perfTestDuration: number) => PerfResult; +} + +export interface PerfAvg { + mean: number; + range: { + min: number; + max: number; + }; +} diff --git a/x-pack/typings/hapi.d.ts b/x-pack/typings/hapi.d.ts index 872e042eb1cdb1..ed86a961cd1db3 100644 --- a/x-pack/typings/hapi.d.ts +++ b/x-pack/typings/hapi.d.ts @@ -10,7 +10,7 @@ import { XPackMainPlugin } from '../legacy/plugins/xpack_main/server/xpack_main' import { SecurityPlugin } from '../legacy/plugins/security'; import { ActionsPlugin, ActionsClient } from '../plugins/actions/server'; import { AlertingPlugin, AlertsClient } from '../plugins/alerting/server'; -import { LegacyTaskManagerApi } from '../legacy/plugins/task_manager/server'; +import { TaskManager } from '../plugins/task_manager/server'; declare module 'hapi' { interface Request { @@ -22,6 +22,6 @@ declare module 'hapi' { security?: SecurityPlugin; actions?: ActionsPlugin; alerting?: AlertingPlugin; - task_manager?: LegacyTaskManagerApi; + task_manager?: TaskManager; } } diff --git a/yarn.lock b/yarn.lock index b28a5b82a83004..1ce919e210e662 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4577,6 +4577,13 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.16.1.tgz#328d1c9b54402e44119398bcb6a31b7bbd606d59" integrity sha512-db6pZL5QY3JrlCHBhYQzYDci0xnoDuxfseUuguLRr3JNk+bnCfpkK6p8quiUDyO8A0vbpBKkk59Fw125etrNeA== +"@types/pretty-ms@^5.0.0": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/pretty-ms/-/pretty-ms-5.0.1.tgz#f2f0d7be58caf8613d149053d446e0282ae11ff3" + integrity sha512-FFR4uj0p47Yq6JCrOt7DCaiUJIw7t9Vh7wwt3bF6qq99QRqjSH/doEGZsIIgZqhDmwjBObVBkrn0ICm1pY+mPg== + dependencies: + pretty-ms "*" + "@types/prop-types@*": version "15.7.1" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6" @@ -4875,6 +4882,11 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== +"@types/stats-lite@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@types/stats-lite/-/stats-lite-2.2.0.tgz#bc8190bf9dfa1e16b89eaa2b433c99dff0804de9" + integrity sha512-YV6SS4QC+pbzqjMIV8qVSTDOOazgKBLTVaN+7PfuxELjz/eyzc20KwDVGPrbHt2OcYMA7K2ezLB45Cp6DpNOSQ== + "@types/strip-ansi@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/strip-ansi/-/strip-ansi-3.0.0.tgz#9b63d453a6b54aa849182207711a08be8eea48ae" @@ -22734,7 +22746,7 @@ parse-link-header@^1.0.1: dependencies: xtend "~4.0.1" -parse-ms@^2.0.0: +parse-ms@^2.0.0, parse-ms@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== @@ -23495,6 +23507,13 @@ pretty-hrtime@^1.0.0, pretty-hrtime@^1.0.3: resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= +pretty-ms@*: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.0.tgz#45781273110caf35f55cab21a8a9bd403a233dc0" + integrity sha512-J3aPWiC5e9ZeZFuSeBraGxSkGMOvulSWsxDByOcbD1Pr75YL3LSNIKIb52WXbCLE1sS5s4inBBbryjF4Y05Ceg== + dependencies: + parse-ms "^2.1.0" + pretty-ms@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-4.0.0.tgz#31baf41b94fd02227098aaa03bd62608eb0d6e92"