diff --git a/x-pack/legacy/plugins/alerting/server/builtin_alert_types/always_firing_default.ts b/x-pack/legacy/plugins/alerting/server/builtin_alert_types/always_firing_default.ts new file mode 100644 index 000000000000..0b0b42ecac34 --- /dev/null +++ b/x-pack/legacy/plugins/alerting/server/builtin_alert_types/always_firing_default.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// This is a "demo" alert type, which always fires it's default action group +// when the executor is run. + +import { AlertExecutorOptions, AlertType } from '../types'; + +export const alertType: AlertType = { + id: '.always-firing-default', + name: 'Alert that always fires the default action group when run', + async executor({ services, params, state }: AlertExecutorOptions) { + services.alertInstanceFactory(`${__filename}:.always-firing`).fire('default', {}); + }, +}; diff --git a/x-pack/legacy/plugins/alerting/server/builtin_alert_types/index.ts b/x-pack/legacy/plugins/alerting/server/builtin_alert_types/index.ts new file mode 100644 index 000000000000..6d71a1cc950b --- /dev/null +++ b/x-pack/legacy/plugins/alerting/server/builtin_alert_types/index.ts @@ -0,0 +1,13 @@ +/* + * 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 { AlertTypeRegistry } from '../alert_type_registry'; + +import { alertType as alwaysFiringDefaultAlertType } from './always_firing_default'; + +export function registerBuiltInAlertTypes(alertTypeRegistry: AlertTypeRegistry) { + alertTypeRegistry.register(alwaysFiringDefaultAlertType); +} diff --git a/x-pack/legacy/plugins/alerting/server/init.ts b/x-pack/legacy/plugins/alerting/server/init.ts index e244a23377fb..6e08380d95ce 100644 --- a/x-pack/legacy/plugins/alerting/server/init.ts +++ b/x-pack/legacy/plugins/alerting/server/init.ts @@ -16,6 +16,7 @@ import { import { AlertingPlugin, Services } from './types'; import { AlertTypeRegistry } from './alert_type_registry'; import { AlertsClient } from './alerts_client'; +import { registerBuiltInAlertTypes } from './builtin_alert_types'; export function init(server: Legacy.Server) { const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin'); @@ -69,4 +70,6 @@ export function init(server: Legacy.Server) { listTypes: alertTypeRegistry.list.bind(alertTypeRegistry), }; server.expose(exposedFunctions); + + registerBuiltInAlertTypes(alertTypeRegistry); } diff --git a/x-pack/legacy/plugins/task_manager/task_manager.ts b/x-pack/legacy/plugins/task_manager/task_manager.ts index 9263747f9964..4d519ce546fa 100644 --- a/x-pack/legacy/plugins/task_manager/task_manager.ts +++ b/x-pack/legacy/plugins/task_manager/task_manager.ts @@ -47,7 +47,8 @@ export class TaskManager { * mechanism. */ public constructor(kbnServer: any, server: any, config: any) { - this.maxWorkers = config.get('xpack.task_manager.max_workers'); + const maxWorkers = config.get('xpack.task_manager.max_workers'); + this.maxWorkers = maxWorkers; this.overrideNumWorkers = config.get('xpack.task_manager.override_num_workers'); this.definitions = {}; @@ -65,7 +66,7 @@ export class TaskManager { }); const pool = new TaskPool({ logger, - maxWorkers: this.maxWorkers, + maxWorkers, }); const createRunner = (instance: ConcreteTaskInstance) => new TaskManagerRunner({ @@ -81,7 +82,11 @@ export class TaskManager { pollInterval: config.get('xpack.task_manager.poll_interval'), store, work(): Promise { - return fillPool(pool.run, store.fetchAvailableTasks, createRunner); + return fillPool( + pool.run, + () => store.fetchAvailableTasks(maxWorkers - pool.occupiedWorkers), + createRunner + ); }, }); diff --git a/x-pack/legacy/plugins/task_manager/task_pool.ts b/x-pack/legacy/plugins/task_manager/task_pool.ts index 8d97bb1068c3..318c70064ba0 100644 --- a/x-pack/legacy/plugins/task_manager/task_pool.ts +++ b/x-pack/legacy/plugins/task_manager/task_pool.ts @@ -67,11 +67,15 @@ export class TaskPool { }; private async attemptToRun(tasks: TaskRunner[]) { - for (const task of tasks) { - if (this.availableWorkers < task.numWorkers) { + let numWorkersToBeUsed = 0; + + const promises = tasks.map(async task => { + if (this.availableWorkers - numWorkersToBeUsed < task.numWorkers) { return false; } + numWorkersToBeUsed += task.numWorkers; + if (await task.claimOwnership()) { this.running.add(task); task @@ -81,9 +85,9 @@ export class TaskPool { }) .then(() => this.running.delete(task)); } - } + }); - return true; + return !(await Promise.all(promises)).includes(false); } private cancelExpiredTasks() { diff --git a/x-pack/legacy/plugins/task_manager/task_store.test.ts b/x-pack/legacy/plugins/task_manager/task_store.test.ts index 91ed8bcad8a6..c12133fe44a9 100644 --- a/x-pack/legacy/plugins/task_manager/task_store.test.ts +++ b/x-pack/legacy/plugins/task_manager/task_store.test.ts @@ -191,7 +191,11 @@ describe('TaskStore', () => { index: 'tasky', body: { sort: [{ 'task.runAt': 'asc' }, { _id: 'desc' }], - query: { term: { type: 'task' } }, + query: { + bool: { + filter: { term: { type: 'task' } }, + }, + }, }, }); }); @@ -207,7 +211,11 @@ describe('TaskStore', () => { body: { query: { bool: { - must: [{ term: { type: 'task' } }, { term: { 'task.taskType': 'bar' } }], + filter: { + bool: { + must: [{ term: { type: 'task' } }, { term: { 'task.taskType': 'bar' } }], + }, + }, }, }, }, @@ -385,19 +393,23 @@ describe('TaskStore', () => { body: { query: { bool: { - must: [ - { term: { type: 'task' } }, - { - bool: { - must: [ - { terms: { 'task.taskType': ['foo', 'bar'] } }, - { range: { 'task.attempts': { lte: maxAttempts } } }, - { range: { 'task.runAt': { lte: 'now' } } }, - { range: { 'kibana.apiVersion': { lte: 1 } } }, - ], - }, + filter: { + bool: { + must: [ + { term: { type: 'task' } }, + { + bool: { + must: [ + { terms: { 'task.taskType': ['foo', 'bar'] } }, + { range: { 'task.attempts': { lte: maxAttempts } } }, + { range: { 'task.runAt': { lte: 'now' } } }, + { range: { 'kibana.apiVersion': { lte: 1 } } }, + ], + }, + }, + ], }, - ], + }, }, }, size: 10, diff --git a/x-pack/legacy/plugins/task_manager/task_store.ts b/x-pack/legacy/plugins/task_manager/task_store.ts index d650f3125dc1..156c6323b89a 100644 --- a/x-pack/legacy/plugins/task_manager/task_store.ts +++ b/x-pack/legacy/plugins/task_manager/task_store.ts @@ -242,7 +242,7 @@ export class TaskStore { id, body, index: this.index, - refresh: true, + refresh: !true, }); const { task } = body; @@ -284,7 +284,7 @@ export class TaskStore { * @prop {number} size - The number of task instances to retrieve * @returns {Promise} */ - public async fetchAvailableTasks(): Promise { + public async fetchAvailableTasks(size: number = 10): Promise { const { docs } = await this.search({ query: { bool: { @@ -296,7 +296,7 @@ export class TaskStore { ], }, }, - size: 10, + size, sort: { 'task.runAt': { order: 'asc' } }, seq_no_primary_term: true, }); @@ -324,7 +324,9 @@ export class TaskStore { if_primary_term: doc.primaryTerm, // The refresh is important so that if we immediately look for work, // we don't pick up this task. - refresh: true, + // However, it also appears to be a performance killer, so shutting it + // off for the moment + refresh: !true, }); return { @@ -346,7 +348,9 @@ export class TaskStore { index: this.index, // The refresh is important so that if we immediately look for work, // we don't pick up this task. - refresh: true, + // However, it also appears to be a performance killer, so shutting it + // off for the moment + refresh: !true, }); return { @@ -370,7 +374,11 @@ export class TaskStore { ignoreUnavailable: true, body: { ...opts, - query, + query: { + bool: { + filter: query, + }, + }, }, }); diff --git a/x-pack/test/api_integration/apis/alerting/MANUAL_TESTING.md b/x-pack/test/api_integration/apis/alerting/MANUAL_TESTING.md new file mode 100644 index 000000000000..1ca2026a6c47 --- /dev/null +++ b/x-pack/test/api_integration/apis/alerting/MANUAL_TESTING.md @@ -0,0 +1,116 @@ +# some notes on manually testing alerts and actions + +## install `kbn-action` + +Some CLI tools to issue Kibana alert and action HTTP requests are available +in this git repo: https://github.com/pmuellr/kbn-action + +To install: + + npm install -g pmuellr/kbn-action + +This will make the commands `kbn-action` and `kbn-alert` available in your +path. + +## running Kibana with `yarn start --no-base-path` + +The `kbn-action` and `kbn-alert` tools require a URL to the Kibana server, +and the normal way of running Kibana via `yarn start` adds a random base +path, to keep developers honest. But means it's a pain to have to reconfigure +these tools every time you run `yarn start`. + +Using the `--no-base-path` option will not add the base path, and the resulting +Kibana URL should be `http://localhost:5601`. + +## configuring `kbn-action/alert` URL + +To configure the tools to use this URL, you can set the following env var + + export KBN_URLBASE=http://elastic:changeme@localhost:5601 + +or use the `-u http://elastic:changeme@localhost:5601` option on the commands. + + +## creating an action to log a message on the server + +```console + $ kbn-action create .server-log "pmuellr server log" '{}' + { + "id": "95de8cfe-e658-4b15-9b02-2e8ab3d0465e" + } + + $ kbn-action fire 95de8cfe-e658-4b15-9b02-2e8ab3d0465e '{message: hallo}' + { + "status": "ok" + } +``` + +After running the above, you should see a message in the server log. + +## creating an alert to run an action every second + +```console +$ kbn-alert create .always-firing-default 1000 '{}' \ + '{group:default id:"95de8cfe-e658-4b15-9b02-2e8ab3d0465e" \ + params:{message: hellllooo}}' +{ + "id": "d13683c0-9dd0-11e9-b59b-e3b7da3ce825", + "alertTypeId": ".always-firing-default", + "interval": 1000, + "actions": [ + { + "group": "default", + "params": { + "message": "hellllooo" + }, + "id": "95de8cfe-e658-4b15-9b02-2e8ab3d0465e" + } + ], + "alertTypeParams": {}, + "scheduledTaskId": "X7qEuWsB5GskuWCOQHjY" +} +``` + +## creating a lot of alerts + +Here's a bash script that creates a bunch of alerts, each printing a +different message: + +```bash +#!/usr/bin/env bash + +export ACTION_ID=95de8cfe-e658-4b15-9b02-2e8ab3d0465e + +for i in {1..2} +do + kbn-alert create .always-firing-default 1000 '{}' \ + "{group:default id:\"${ACTION_ID}\" params:{message: \"hellllooo ${i}\"}}" +done +``` + +## deleting a lot of alerts + +Use [`jq`](https://stedolan.github.io/jq/), install on mac with `brew`: + + brew install jq + +Because the `kbn-a*` commands write JSON, `jq` is the tool of choice to cut +things up. + +To get the ids of all the current alerts: + +```console +$ kbn-alert ls | jq ".[ ] | .id" +"d13683c0-9dd0-11e9-b59b-e3b7da3ce825" +"f346cd20-9dd1-11e9-b59b-e3b7da3ce825" +"f46a1770-9dd1-11e9-b59b-e3b7da3ce825" +``` + +To delete them, sprinkle on some `xargs`: + +```console +$ kbn-alert ls | jq ".[] | .id" | xargs -L 1 kbn-alert delete +{} +{} +{} +``` \ No newline at end of file