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

Adds event log for actions and alerting #45081

Merged
merged 11 commits into from
Jan 21, 2020
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
# Kibana Alerting Services
/x-pack/legacy/plugins/alerting/ @elastic/kibana-alerting-services
/x-pack/legacy/plugins/actions/ @elastic/kibana-alerting-services
/x-pack/plugins/event_log/ @elastic/kibana-alerting-services
/x-pack/plugins/task_manager/ @elastic/kibana-alerting-services
/x-pack/test/alerting_api_integration/ @elastic/kibana-alerting-services
/x-pack/test/plugin_api_integration/plugins/task_manager/ @elastic/kibana-alerting-services
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
savedObjectsClientMock,
loggingServiceMock,
} from '../../../../../../src/core/server/mocks';
import { createEventLoggerMock } from '../../../../../plugins/event_log/server/event_logger.mock';

const actionExecutor = new ActionExecutor();
const savedObjectsClient = savedObjectsClientMock.create();
Expand Down Expand Up @@ -58,6 +59,7 @@ actionExecutor.initialize({
getServices,
actionTypeRegistry,
encryptedSavedObjectsPlugin,
eventLogger: createEventLoggerMock(),
});

beforeEach(() => jest.resetAllMocks());
Expand Down
70 changes: 57 additions & 13 deletions x-pack/legacy/plugins/actions/server/lib/action_executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ import {
GetServicesFunction,
RawAction,
} from '../types';
import { EVENT_LOG_ACTIONS } from '../plugin';
import { IEvent, IEventLogger } from '../../../../../plugins/event_log/server';

export interface ActionExecutorContext {
logger: Logger;
spaces: () => SpacesPluginStartContract | undefined;
getServices: GetServicesFunction;
encryptedSavedObjectsPlugin: EncryptedSavedObjectsStartContract;
actionTypeRegistry: ActionTypeRegistryContract;
eventLogger: IEventLogger;
}

export interface ExecuteOptions {
Expand Down Expand Up @@ -54,11 +57,11 @@ export class ActionExecutor {
}

const {
logger,
spaces,
getServices,
encryptedSavedObjectsPlugin,
actionTypeRegistry,
eventLogger,
} = this.actionExecutorContext!;

const spacesPlugin = spaces();
Expand Down Expand Up @@ -89,9 +92,9 @@ export class ActionExecutor {
);
const actionType = actionTypeRegistry.get(actionTypeId);

let validatedParams;
let validatedConfig;
let validatedSecrets;
let validatedParams: Record<string, any>;
let validatedConfig: Record<string, any>;
let validatedSecrets: Record<string, any>;

try {
validatedParams = validateParams(actionType, params);
Expand All @@ -101,27 +104,68 @@ export class ActionExecutor {
return { status: 'error', actionId, message: err.message, retry: false };
}

let result: ActionTypeExecutorResult | null = null;
const actionLabel = `${actionId} - ${actionTypeId} - ${name}`;
const actionLabel = `${actionTypeId}:${actionId}: ${name}`;
const event: IEvent = {
event: { action: EVENT_LOG_ACTIONS.execute },
kibana: { namespace, saved_objects: [{ type: 'action', id: actionId }] },
};

eventLogger.startTiming(event);
let rawResult: ActionTypeExecutorResult | null | undefined | void;
try {
result = await actionType.executor({
rawResult = await actionType.executor({
actionId,
services,
params: validatedParams,
config: validatedConfig,
secrets: validatedSecrets,
});
} catch (err) {
logger.warn(`action executed unsuccessfully: ${actionLabel} - ${err.message}`);
throw err;
rawResult = {
actionId,
status: 'error',
message: 'an error occurred while running the action executor',
serviceMessage: err.message,
retry: false,
};
}
eventLogger.stopTiming(event);

logger.debug(`action executed successfully: ${actionLabel}`);

// return basic response if none provided
if (result == null) return { status: 'ok', actionId };
// allow null-ish return to indicate success
const result = rawResult || {
actionId,
status: 'ok',
};

if (result.status === 'ok') {
event.message = `action executed: ${actionLabel}`;
} else if (result.status === 'error') {
event.message = `action execution failure: ${actionLabel}`;
event.error = event.error || {};
event.error.message = actionErrorToMessage(result);
} else {
event.message = `action execution returned unexpected result: ${actionLabel}`;
event.error = event.error || {};
event.error.message = 'action execution returned unexpected result';
}

eventLogger.logEvent(event);
return result;
}
}

function actionErrorToMessage(result: ActionTypeExecutorResult): string {
let message = result.message || 'unknown error running action';

if (result.serviceMessage) {
message = `${message}: ${result.serviceMessage}`;
}

if (result.retry instanceof Date) {
message = `${message}; retry at ${result.retry.toISOString()}`;
} else if (result.retry) {
message = `${message}; retry: ${JSON.stringify(result.retry)}`;
}

return message;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
savedObjectsClientMock,
loggingServiceMock,
} from '../../../../../../src/core/server/mocks';
import { createEventLoggerMock } from '../../../../../plugins/event_log/server/event_logger.mock';

const spaceIdToNamespace = jest.fn();
const actionTypeRegistry = actionTypeRegistryMock.create();
Expand Down Expand Up @@ -62,6 +63,7 @@ const actionExecutorInitializerParams = {
actionTypeRegistry,
spaces: () => undefined,
encryptedSavedObjectsPlugin: mockedEncryptedSavedObjectsPlugin,
eventLogger: createEventLoggerMock(),
};
const taskRunnerFactoryInitializerParams = {
spaceIdToNamespace,
Expand Down
14 changes: 14 additions & 0 deletions x-pack/legacy/plugins/actions/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ import {
} from './routes';
import { extendRouteWithLicenseCheck } from './extend_route_with_license_check';
import { LicenseState } from './lib/license_state';
import { IEventLogger } from '../../../../plugins/event_log/server';

const EVENT_LOG_PROVIDER = 'actions';
export const EVENT_LOG_ACTIONS = {
execute: 'execute',
executeViaHttp: 'execute-via-http',
};

export interface PluginSetupContract {
registerType: ActionTypeRegistry['register'];
Expand All @@ -57,6 +64,7 @@ export class Plugin {
private actionExecutor?: ActionExecutor;
private defaultKibanaIndex?: string;
private licenseState: LicenseState | null = null;
private eventLogger?: IEventLogger;

constructor(initializerContext: ActionsPluginInitializerContext) {
this.logger = initializerContext.logger.get('plugins', 'actions');
Expand Down Expand Up @@ -88,6 +96,11 @@ export class Plugin {
attributesToEncrypt: new Set(['apiKey']),
});

plugins.event_log.registerProviderActions(EVENT_LOG_PROVIDER, Object.values(EVENT_LOG_ACTIONS));
this.eventLogger = plugins.event_log.getLogger({
event: { provider: EVENT_LOG_PROVIDER },
});

const actionExecutor = new ActionExecutor();
const taskRunnerFactory = new TaskRunnerFactory(actionExecutor);
const actionsConfigUtils = getActionsConfigurationUtilities(config as ActionsConfigType);
Expand Down Expand Up @@ -156,6 +169,7 @@ export class Plugin {
getServices,
encryptedSavedObjectsPlugin: plugins.encryptedSavedObjects,
actionTypeRegistry: actionTypeRegistry!,
eventLogger: this.eventLogger!,
});
taskRunnerFactory!.initialize({
encryptedSavedObjectsPlugin: plugins.encryptedSavedObjects,
Expand Down
3 changes: 3 additions & 0 deletions x-pack/legacy/plugins/actions/server/shim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
SavedObjectsLegacyService,
} from '../../../../../src/core/server';
import { LicensingPluginSetup } from '../../../../plugins/licensing/server';
import { IEventLogService } from '../../../../plugins/event_log/server';

export interface KibanaConfig {
index: string;
Expand Down Expand Up @@ -67,6 +68,7 @@ export interface ActionsPluginsSetup {
xpack_main: XPackMainPluginSetupContract;
encryptedSavedObjects: EncryptedSavedObjectsSetupContract;
licensing: LicensingPluginSetup;
event_log: IEventLogService;
}
export interface ActionsPluginsStart {
security?: SecurityPluginStartContract;
Expand Down Expand Up @@ -126,6 +128,7 @@ export function shim(
encryptedSavedObjects: newPlatform.setup.plugins
.encryptedSavedObjects as EncryptedSavedObjectsSetupContract,
licensing: newPlatform.setup.plugins.licensing as LicensingPluginSetup,
event_log: newPlatform.setup.plugins.event_log as IEventLogService,
};

const pluginsStart: ActionsPluginsStart = {
Expand Down
2 changes: 1 addition & 1 deletion x-pack/legacy/plugins/actions/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export interface ActionTypeExecutorResult {
// signature of the action type executor function
export type ExecutorType = (
options: ActionTypeExecutorOptions
) => Promise<ActionTypeExecutorResult>;
) => Promise<ActionTypeExecutorResult | null | undefined | void>;

interface ValidatorType {
validate<T>(value: any): any;
Expand Down
Loading