From 249818d45abae196cf6ea9c5ca29db072d98f7fb Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 15 Apr 2021 17:26:34 -0400 Subject: [PATCH] [Actions][Telemetry] Counting number of alert history connectors in use (#97063) (#97300) * Counting number of alert history connectors in use * Telemetry for preconfigured alert history config enabled * Updating telemetry mappings * Updating tests * Adding descriptions to new telemetry fields Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: ymao1 --- x-pack/plugins/actions/server/plugin.ts | 1 + .../server/usage/actions_telemetry.test.ts | 67 +++++++++++++++++++ .../actions/server/usage/actions_telemetry.ts | 26 ++++++- .../usage/actions_usage_collector.test.ts | 5 ++ .../server/usage/actions_usage_collector.ts | 18 ++++- x-pack/plugins/actions/server/usage/task.ts | 1 + x-pack/plugins/actions/server/usage/types.ts | 2 + .../schema/xpack_plugins.json | 12 ++++ 8 files changed, 128 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 3c754d90c4af5..1d941617789b7 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -224,6 +224,7 @@ export class ActionsPlugin implements Plugin taskManager) ); } diff --git a/x-pack/plugins/actions/server/usage/actions_telemetry.test.ts b/x-pack/plugins/actions/server/usage/actions_telemetry.test.ts index e4611857ca279..18510ba9aa864 100644 --- a/x-pack/plugins/actions/server/usage/actions_telemetry.test.ts +++ b/x-pack/plugins/actions/server/usage/actions_telemetry.test.ts @@ -158,12 +158,79 @@ Object { expect(telemetry).toMatchInlineSnapshot(` Object { + "countByAlertHistoryConnectorType": 0, "countByType": Object { "__server-log": 1, "__slack": 1, }, "countTotal": 2, } +`); + }); + + test('getInUseTotalCount should count preconfigured alert history connector usage', async () => { + const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; + mockEsClient.search.mockReturnValue( + // @ts-expect-error not full search response + elasticsearchClientMock.createSuccessTransportRequestPromise({ + aggregations: { + refs: { + actionRefIds: { + value: { + connectorIds: { + '1': 'action_0', + '123': 'action_1', + 'preconfigured-alert-history-es-index': 'action_2', + }, + total: 3, + }, + }, + }, + hits: { + hits: [], + }, + }, + }) + ); + const actionsBulkGet = jest.fn(); + actionsBulkGet.mockReturnValue({ + saved_objects: [ + { + id: '1', + attributes: { + actionTypeId: '.server-log', + }, + }, + { + id: '123', + attributes: { + actionTypeId: '.slack', + }, + }, + { + id: 'preconfigured-alert-history-es-index', + error: { + statusCode: 404, + error: 'Not Found', + message: 'Saved object [action/preconfigured-alert-history-es-index] not found', + }, + }, + ], + }); + const telemetry = await getInUseTotalCount(mockEsClient, actionsBulkGet, 'test'); + + expect(mockEsClient.search).toHaveBeenCalledTimes(1); + expect(actionsBulkGet).toHaveBeenCalledTimes(1); + + expect(telemetry).toMatchInlineSnapshot(` +Object { + "countByAlertHistoryConnectorType": 1, + "countByType": Object { + "__server-log": 1, + "__slack": 1, + }, + "countTotal": 3, +} `); }); }); diff --git a/x-pack/plugins/actions/server/usage/actions_telemetry.ts b/x-pack/plugins/actions/server/usage/actions_telemetry.ts index 8d028b176a00a..71516cb4918e7 100644 --- a/x-pack/plugins/actions/server/usage/actions_telemetry.ts +++ b/x-pack/plugins/actions/server/usage/actions_telemetry.ts @@ -11,6 +11,7 @@ import { SavedObjectsBulkGetObject, SavedObjectsBulkResponse, } from 'kibana/server'; +import { AlertHistoryEsIndexConnectorId } from '../../common'; import { ActionResult } from '../types'; export async function getTotalCount(esClient: ElasticsearchClient, kibanaIndex: string) { @@ -79,7 +80,11 @@ export async function getInUseTotalCount( options?: SavedObjectsBaseOptions | undefined ) => Promise>>>, kibanaIndex: string -): Promise<{ countTotal: number; countByType: Record }> { +): Promise<{ + countTotal: number; + countByType: Record; + countByAlertHistoryConnectorType: number; +}> { const scriptedMetric = { scripted_metric: { init_script: 'state.connectorIds = new HashMap(); state.total = 0;', @@ -167,7 +172,13 @@ export async function getInUseTotalCount( fields: ['id', 'actionTypeId'], })); const actions = await actionsBulkGet(bulkFilter); - const countByType = actions.saved_objects.reduce( + + // filter out preconfigured connectors, which are not saved objects and return + // an error in the bulk response + const actionsWithActionTypeId = actions.saved_objects.filter( + (action) => action?.attributes?.actionTypeId != null + ); + const countByActionTypeId = actionsWithActionTypeId.reduce( (actionTypeCount: Record, action) => { const alertTypeId = replaceFirstAndLastDotSymbols(action.attributes.actionTypeId); const currentCount = @@ -177,7 +188,16 @@ export async function getInUseTotalCount( }, {} ); - return { countTotal: aggs.total, countByType }; + + const preconfiguredAlertHistoryConnector = actions.saved_objects.filter( + (action) => action.id === AlertHistoryEsIndexConnectorId + ); + + return { + countTotal: aggs.total, + countByType: countByActionTypeId, + countByAlertHistoryConnectorType: preconfiguredAlertHistoryConnector.length, + }; } function replaceFirstAndLastDotSymbols(strToReplace: string) { diff --git a/x-pack/plugins/actions/server/usage/actions_usage_collector.test.ts b/x-pack/plugins/actions/server/usage/actions_usage_collector.test.ts index cf76adddc5494..df43cbd084be8 100644 --- a/x-pack/plugins/actions/server/usage/actions_usage_collector.test.ts +++ b/x-pack/plugins/actions/server/usage/actions_usage_collector.test.ts @@ -7,6 +7,7 @@ import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { registerActionsUsageCollector } from './actions_usage_collector'; +import { configSchema, ActionsConfig } from '../config'; import { taskManagerMock } from '../../../task_manager/server/mocks'; const mockTaskManagerStart = taskManagerMock.createStart(); @@ -14,8 +15,10 @@ const mockTaskManagerStart = taskManagerMock.createStart(); beforeEach(() => jest.resetAllMocks()); describe('registerActionsUsageCollector', () => { + let config: ActionsConfig; let usageCollectionMock: jest.Mocked; beforeEach(() => { + config = configSchema.validate({}); usageCollectionMock = ({ makeUsageCollector: jest.fn(), registerCollector: jest.fn(), @@ -25,6 +28,7 @@ describe('registerActionsUsageCollector', () => { it('should call registerCollector', () => { registerActionsUsageCollector( usageCollectionMock as UsageCollectionSetup, + config, new Promise(() => mockTaskManagerStart) ); expect(usageCollectionMock.registerCollector).toHaveBeenCalledTimes(1); @@ -33,6 +37,7 @@ describe('registerActionsUsageCollector', () => { it('should call makeUsageCollector with type = actions', () => { registerActionsUsageCollector( usageCollectionMock as UsageCollectionSetup, + config, new Promise(() => mockTaskManagerStart) ); expect(usageCollectionMock.makeUsageCollector).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/actions/server/usage/actions_usage_collector.ts b/x-pack/plugins/actions/server/usage/actions_usage_collector.ts index c338bbc998c49..06248e1fa95a8 100644 --- a/x-pack/plugins/actions/server/usage/actions_usage_collector.ts +++ b/x-pack/plugins/actions/server/usage/actions_usage_collector.ts @@ -9,6 +9,7 @@ import { MakeSchemaFrom, UsageCollectionSetup } from 'src/plugins/usage_collecti import { get } from 'lodash'; import { TaskManagerStartContract } from '../../../task_manager/server'; import { ActionsUsage } from './types'; +import { ActionsConfig } from '../config'; const byTypeSchema: MakeSchemaFrom['count_by_type'] = { // TODO: Find out an automated way to populate the keys or reformat these into an array (and change the Remote Telemetry indexer accordingly) @@ -28,6 +29,7 @@ const byTypeSchema: MakeSchemaFrom['count_by_type'] = { export function createActionsUsageCollector( usageCollection: UsageCollectionSetup, + config: ActionsConfig, taskManager: Promise ) { return usageCollection.makeUsageCollector({ @@ -37,8 +39,18 @@ export function createActionsUsageCollector( return true; }, schema: { + alert_history_connector_enabled: { + type: 'boolean', + _meta: { description: 'Indicates if preconfigured alert history connector is enabled.' }, + }, count_total: { type: 'long' }, count_active_total: { type: 'long' }, + count_active_alert_history_connectors: { + type: 'long', + _meta: { + description: 'The total number of preconfigured alert history connectors used by rules.', + }, + }, count_by_type: byTypeSchema, count_active_by_type: byTypeSchema, }, @@ -50,11 +62,14 @@ export function createActionsUsageCollector( return { ...state, + alert_history_connector_enabled: config.preconfiguredAlertHistoryEsIndex, }; } catch (err) { return { + alert_history_connector_enabled: false, count_total: 0, count_active_total: 0, + count_active_alert_history_connectors: 0, count_active_by_type: {}, count_by_type: {}, }; @@ -84,8 +99,9 @@ async function getLatestTaskState(taskManager: TaskManagerStartContract) { export function registerActionsUsageCollector( usageCollection: UsageCollectionSetup, + config: ActionsConfig, taskManager: Promise ) { - const collector = createActionsUsageCollector(usageCollection, taskManager); + const collector = createActionsUsageCollector(usageCollection, config, taskManager); usageCollection.registerCollector(collector); } diff --git a/x-pack/plugins/actions/server/usage/task.ts b/x-pack/plugins/actions/server/usage/task.ts index 03c49a31ed311..3ba40d92abd7a 100644 --- a/x-pack/plugins/actions/server/usage/task.ts +++ b/x-pack/plugins/actions/server/usage/task.ts @@ -101,6 +101,7 @@ export function telemetryTaskRunner(logger: Logger, core: CoreSetup, kibanaIndex count_by_type: totalAggegations.countByType, count_active_total: totalInUse.countTotal, count_active_by_type: totalInUse.countByType, + count_active_alert_history_connectors: totalInUse.countByAlertHistoryConnectorType, }, runAt: getNextMidnight(), }; diff --git a/x-pack/plugins/actions/server/usage/types.ts b/x-pack/plugins/actions/server/usage/types.ts index c192115bfb02a..9221ba8ea5688 100644 --- a/x-pack/plugins/actions/server/usage/types.ts +++ b/x-pack/plugins/actions/server/usage/types.ts @@ -6,8 +6,10 @@ */ export interface ActionsUsage { + alert_history_connector_enabled: boolean; count_total: number; count_active_total: number; + count_active_alert_history_connectors: number; count_by_type: Record; count_active_by_type: Record; // TODO: Implement executions count telemetry with eventLog, when it will write to index diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index 3d302aa12832e..1d1cd8c0c7667 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -2,12 +2,24 @@ "properties": { "actions": { "properties": { + "alert_history_connector_enabled": { + "type": "boolean", + "_meta": { + "description": "Indicates if preconfigured alert history connector is enabled." + } + }, "count_total": { "type": "long" }, "count_active_total": { "type": "long" }, + "count_active_alert_history_connectors": { + "type": "long", + "_meta": { + "description": "The total number of preconfigured alert history connectors used by rules." + } + }, "count_by_type": { "properties": { "DYNAMIC_KEY": {