Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Actions][Telemetry] Counting number of alert history connectors in use #97063

Merged
merged 8 commits into from
Apr 15, 2021
1 change: 1 addition & 0 deletions x-pack/plugins/actions/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
if (usageCollection) {
registerActionsUsageCollector(
usageCollection,
this.actionsConfig,
core.getStartServices().then(([_, { taskManager }]) => taskManager)
);
}
Expand Down
67 changes: 67 additions & 0 deletions x-pack/plugins/actions/server/usage/actions_telemetry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
`);
});
});
26 changes: 23 additions & 3 deletions x-pack/plugins/actions/server/usage/actions_telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -79,7 +80,11 @@ export async function getInUseTotalCount(
options?: SavedObjectsBaseOptions | undefined
) => Promise<SavedObjectsBulkResponse<ActionResult<Record<string, unknown>>>>,
kibanaIndex: string
): Promise<{ countTotal: number; countByType: Record<string, number> }> {
): Promise<{
countTotal: number;
countByType: Record<string, number>;
countByAlertHistoryConnectorType: number;
}> {
const scriptedMetric = {
scripted_metric: {
init_script: 'state.connectorIds = new HashMap(); state.total = 0;',
Expand Down Expand Up @@ -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<string, number>, action) => {
const alertTypeId = replaceFirstAndLastDotSymbols(action.attributes.actionTypeId);
const currentCount =
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@

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();

beforeEach(() => jest.resetAllMocks());

describe('registerActionsUsageCollector', () => {
let config: ActionsConfig;
let usageCollectionMock: jest.Mocked<UsageCollectionSetup>;
beforeEach(() => {
config = configSchema.validate({});
usageCollectionMock = ({
makeUsageCollector: jest.fn(),
registerCollector: jest.fn(),
Expand All @@ -25,6 +28,7 @@ describe('registerActionsUsageCollector', () => {
it('should call registerCollector', () => {
registerActionsUsageCollector(
usageCollectionMock as UsageCollectionSetup,
config,
new Promise(() => mockTaskManagerStart)
);
expect(usageCollectionMock.registerCollector).toHaveBeenCalledTimes(1);
Expand All @@ -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);
Expand Down
18 changes: 17 additions & 1 deletion x-pack/plugins/actions/server/usage/actions_usage_collector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ActionsUsage>['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)
Expand All @@ -28,6 +29,7 @@ const byTypeSchema: MakeSchemaFrom<ActionsUsage>['count_by_type'] = {

export function createActionsUsageCollector(
usageCollection: UsageCollectionSetup,
config: ActionsConfig,
taskManager: Promise<TaskManagerStartContract>
) {
return usageCollection.makeUsageCollector<ActionsUsage>({
Expand All @@ -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,
},
Expand All @@ -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: {},
};
Expand Down Expand Up @@ -84,8 +99,9 @@ async function getLatestTaskState(taskManager: TaskManagerStartContract) {

export function registerActionsUsageCollector(
usageCollection: UsageCollectionSetup,
config: ActionsConfig,
taskManager: Promise<TaskManagerStartContract>
) {
const collector = createActionsUsageCollector(usageCollection, taskManager);
const collector = createActionsUsageCollector(usageCollection, config, taskManager);
usageCollection.registerCollector(collector);
}
1 change: 1 addition & 0 deletions x-pack/plugins/actions/server/usage/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
};
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/actions/server/usage/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, number>;
count_active_by_type: Record<string, number>;
// TODO: Implement executions count telemetry with eventLog, when it will write to index
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down