mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
## Summary [Internal link](https://github.com/elastic/security-team/issues/10820) to the feature details Part of https://github.com/elastic/security-team/issues/11232 This PR covers SIEM Migrations Stats APIs: * Retrieves the stats for the specific migration: (route: `GET /internal/siem_migrations/rules/{migration_id}/stat`) * Retrieves the stats for all the existing migrations, aggregated by `migration_id`: (route: `GET /internal/siem_migrations/rules/stats`) * Retrieves the translation stats for the migration: (route: `GET /internal/siem_migrations/rules/{migration_id}/translation_stats`)
This commit is contained in:
parent
89b05458ad
commit
6006546dc4
5 changed files with 270 additions and 14 deletions
|
@ -52,7 +52,7 @@ export interface GetRuleMigrationStatsParams {
|
|||
/** Optional AbortSignal for cancelling request */
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
/** Retrieves the stats for all the existing migrations, aggregated by `migration_id`. */
|
||||
/** Retrieves the stats for the specific migration. */
|
||||
export const getRuleMigrationStats = async ({
|
||||
migrationId,
|
||||
signal,
|
||||
|
|
|
@ -10,6 +10,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
|
|||
describe('@ess SecuritySolution SIEM Migrations', () => {
|
||||
loadTestFile(require.resolve('./create'));
|
||||
loadTestFile(require.resolve('./get'));
|
||||
loadTestFile(require.resolve('./stats'));
|
||||
loadTestFile(require.resolve('./update'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import {
|
||||
createMigrationRules,
|
||||
deleteAllMigrationRules,
|
||||
getMigrationRuleDocuments,
|
||||
migrationRulesRouteHelpersFactory,
|
||||
statsOverrideCallbackFactory,
|
||||
} from '../../utils';
|
||||
import { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const es = getService('es');
|
||||
const supertest = getService('supertest');
|
||||
const migrationRulesRoutes = migrationRulesRouteHelpersFactory(supertest);
|
||||
|
||||
describe('@ess @serverless @serverlessQA Stats API', () => {
|
||||
beforeEach(async () => {
|
||||
await deleteAllMigrationRules(es);
|
||||
});
|
||||
|
||||
it('should return stats for the specific migration', async () => {
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const failed = 3;
|
||||
const pending = 5;
|
||||
const processing = 7;
|
||||
const completed = 10;
|
||||
const total = failed + pending + processing + completed;
|
||||
const overrideCallback = statsOverrideCallbackFactory({
|
||||
migrationId,
|
||||
failed,
|
||||
pending,
|
||||
processing,
|
||||
completed, // 4 - full, 5 - partial, 1 - untranslated
|
||||
fullyTranslated: 4,
|
||||
partiallyTranslated: 5,
|
||||
});
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(total, overrideCallback);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
const response = await migrationRulesRoutes.stats({ migrationId });
|
||||
expect(response.body).toEqual(
|
||||
expect.objectContaining({
|
||||
status: 'stopped',
|
||||
id: migrationId,
|
||||
rules: {
|
||||
total,
|
||||
pending,
|
||||
processing,
|
||||
completed,
|
||||
failed,
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should return stats for the existing migrations', async () => {
|
||||
const migrationId1 = uuidv4();
|
||||
const migrationId2 = uuidv4();
|
||||
|
||||
const overrideCallback1 = statsOverrideCallbackFactory({
|
||||
migrationId: migrationId1,
|
||||
failed: 2,
|
||||
pending: 4,
|
||||
processing: 3,
|
||||
completed: 33,
|
||||
fullyTranslated: 10,
|
||||
partiallyTranslated: 10,
|
||||
});
|
||||
const migrationRuleDocuments1 = getMigrationRuleDocuments(42, overrideCallback1);
|
||||
const overrideCallback2 = statsOverrideCallbackFactory({
|
||||
migrationId: migrationId2,
|
||||
failed: 7,
|
||||
pending: 2,
|
||||
processing: 5,
|
||||
completed: 14,
|
||||
fullyTranslated: 3,
|
||||
partiallyTranslated: 5,
|
||||
});
|
||||
const migrationRuleDocuments2 = getMigrationRuleDocuments(28, overrideCallback2);
|
||||
await createMigrationRules(es, [...migrationRuleDocuments1, ...migrationRuleDocuments2]);
|
||||
|
||||
const response = await migrationRulesRoutes.statsAll({});
|
||||
const expectedStats = expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
status: 'stopped',
|
||||
id: migrationId1,
|
||||
rules: { total: 42, pending: 4, processing: 3, completed: 33, failed: 2 },
|
||||
}),
|
||||
expect.objectContaining({
|
||||
status: 'stopped',
|
||||
id: migrationId2,
|
||||
rules: { total: 28, pending: 2, processing: 5, completed: 14, failed: 7 },
|
||||
}),
|
||||
]);
|
||||
expect(response.body).toEqual(expectedStats);
|
||||
});
|
||||
|
||||
it('should return translation stats for the specific migration', async () => {
|
||||
const migrationId = uuidv4();
|
||||
|
||||
const failed = 3;
|
||||
const pending = 5;
|
||||
const processing = 7;
|
||||
const completed = 10;
|
||||
const total = failed + pending + processing + completed;
|
||||
const overrideCallback = statsOverrideCallbackFactory({
|
||||
migrationId,
|
||||
failed,
|
||||
pending,
|
||||
processing,
|
||||
completed, // 4 - full, 5 - partial, 1 - untranslated
|
||||
fullyTranslated: 4,
|
||||
partiallyTranslated: 5,
|
||||
});
|
||||
const migrationRuleDocuments = getMigrationRuleDocuments(total, overrideCallback);
|
||||
await createMigrationRules(es, migrationRuleDocuments);
|
||||
|
||||
const response = await migrationRulesRoutes.translationStats({ migrationId });
|
||||
expect(response.body).toEqual(
|
||||
expect.objectContaining({
|
||||
id: migrationId,
|
||||
rules: {
|
||||
total,
|
||||
success: {
|
||||
total: completed,
|
||||
result: { full: 4, partial: 5, untranslatable: 1 },
|
||||
installable: 4,
|
||||
prebuilt: 0,
|
||||
},
|
||||
failed,
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
|
@ -6,6 +6,10 @@
|
|||
*/
|
||||
|
||||
import type { Client } from '@elastic/elasticsearch';
|
||||
import {
|
||||
RuleTranslationResult,
|
||||
SiemMigrationStatus,
|
||||
} from '@kbn/security-solution-plugin/common/siem_migrations/constants';
|
||||
|
||||
import {
|
||||
ElasticRule,
|
||||
|
@ -95,6 +99,58 @@ export const getMigrationRuleDocuments = (
|
|||
return docs;
|
||||
};
|
||||
|
||||
export const statsOverrideCallbackFactory = ({
|
||||
migrationId,
|
||||
failed,
|
||||
pending,
|
||||
processing,
|
||||
completed,
|
||||
fullyTranslated,
|
||||
partiallyTranslated,
|
||||
}: {
|
||||
migrationId: string;
|
||||
failed: number;
|
||||
pending: number;
|
||||
processing: number;
|
||||
completed: number;
|
||||
fullyTranslated: number;
|
||||
partiallyTranslated: number;
|
||||
}) => {
|
||||
const overrideCallback = (index: number): Partial<RuleMigrationDocument> => {
|
||||
let translationResult;
|
||||
let status = SiemMigrationStatus.PENDING;
|
||||
|
||||
const pendingEndIndex = failed + pending;
|
||||
const processingEndIndex = failed + pending + processing;
|
||||
const completedEndIndex = failed + pending + processing + completed;
|
||||
if (index < failed) {
|
||||
status = SiemMigrationStatus.FAILED;
|
||||
} else if (index < pendingEndIndex) {
|
||||
status = SiemMigrationStatus.PENDING;
|
||||
} else if (index < processingEndIndex) {
|
||||
status = SiemMigrationStatus.PROCESSING;
|
||||
} else if (index < completedEndIndex) {
|
||||
status = SiemMigrationStatus.COMPLETED;
|
||||
const fullyTranslatedEndIndex = completedEndIndex - completed + fullyTranslated;
|
||||
const partiallyTranslatedEndIndex =
|
||||
completedEndIndex - completed + fullyTranslated + partiallyTranslated;
|
||||
if (index < fullyTranslatedEndIndex) {
|
||||
translationResult = RuleTranslationResult.FULL;
|
||||
} else if (index < partiallyTranslatedEndIndex) {
|
||||
translationResult = RuleTranslationResult.PARTIAL;
|
||||
} else {
|
||||
translationResult = RuleTranslationResult.UNTRANSLATABLE;
|
||||
}
|
||||
}
|
||||
return {
|
||||
migration_id: migrationId,
|
||||
translation_result: translationResult,
|
||||
status,
|
||||
};
|
||||
};
|
||||
return overrideCallback;
|
||||
};
|
||||
|
||||
export const createMigrationRules = async (
|
||||
es: Client,
|
||||
rules: RuleMigrationDocument[]
|
||||
|
|
|
@ -13,44 +13,49 @@ import {
|
|||
import { replaceParams } from '@kbn/openapi-common/shared';
|
||||
|
||||
import {
|
||||
SIEM_RULE_MIGRATIONS_ALL_STATS_PATH,
|
||||
SIEM_RULE_MIGRATIONS_PATH,
|
||||
SIEM_RULE_MIGRATION_PATH,
|
||||
SIEM_RULE_MIGRATION_STATS_PATH,
|
||||
SIEM_RULE_MIGRATION_TRANSLATION_STATS_PATH,
|
||||
} from '@kbn/security-solution-plugin/common/siem_migrations/constants';
|
||||
import {
|
||||
CreateRuleMigrationResponse,
|
||||
GetAllStatsRuleMigrationResponse,
|
||||
GetRuleMigrationRequestQuery,
|
||||
GetRuleMigrationResponse,
|
||||
GetRuleMigrationStatsResponse,
|
||||
UpdateRuleMigrationResponse,
|
||||
} from '@kbn/security-solution-plugin/common/siem_migrations/model/api/rules/rule_migration.gen';
|
||||
import { API_VERSIONS } from '@kbn/security-solution-plugin/common/constants';
|
||||
import { assertStatusCode } from './asserts';
|
||||
|
||||
export interface GetRuleMigrationParams {
|
||||
/** `id` of the migration to get rules documents for */
|
||||
migrationId: string;
|
||||
/** Optional query parameters */
|
||||
queryParams?: GetRuleMigrationRequestQuery;
|
||||
export interface RequestParams {
|
||||
/** Optional expected status code parameter */
|
||||
expectStatusCode?: number;
|
||||
}
|
||||
|
||||
export interface CreateRuleMigrationParams {
|
||||
export interface MigrationRequestParams extends RequestParams {
|
||||
/** `id` of the migration to get rules documents for */
|
||||
migrationId: string;
|
||||
}
|
||||
|
||||
export interface GetRuleMigrationParams extends MigrationRequestParams {
|
||||
/** Optional query parameters */
|
||||
queryParams?: GetRuleMigrationRequestQuery;
|
||||
}
|
||||
|
||||
export interface CreateRuleMigrationParams extends RequestParams {
|
||||
/** Optional `id` of migration to add the rules to.
|
||||
* The id is necessary only for batching the migration creation in multiple requests */
|
||||
migrationId?: string;
|
||||
/** Optional payload to send */
|
||||
payload?: any;
|
||||
/** Optional expected status code parameter */
|
||||
expectStatusCode?: number;
|
||||
}
|
||||
|
||||
export interface UpdateRulesParams {
|
||||
/** `id` of the migration to install rules for */
|
||||
migrationId: string;
|
||||
export interface UpdateRulesParams extends MigrationRequestParams {
|
||||
/** Optional payload to send */
|
||||
payload?: any;
|
||||
/** Optional expected status code parameter */
|
||||
expectStatusCode?: number;
|
||||
}
|
||||
|
||||
export const migrationRulesRouteHelpersFactory = (supertest: SuperTest.Agent) => {
|
||||
|
@ -106,5 +111,54 @@ export const migrationRulesRouteHelpersFactory = (supertest: SuperTest.Agent) =>
|
|||
|
||||
return response;
|
||||
},
|
||||
|
||||
stats: async ({
|
||||
migrationId,
|
||||
expectStatusCode = 200,
|
||||
}: MigrationRequestParams): Promise<{ body: GetRuleMigrationStatsResponse }> => {
|
||||
const response = await supertest
|
||||
.get(replaceParams(SIEM_RULE_MIGRATION_STATS_PATH, { migration_id: migrationId }))
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set(ELASTIC_HTTP_VERSION_HEADER, API_VERSIONS.internal.v1)
|
||||
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
||||
.send();
|
||||
|
||||
assertStatusCode(expectStatusCode, response);
|
||||
|
||||
return response;
|
||||
},
|
||||
|
||||
statsAll: async ({
|
||||
expectStatusCode = 200,
|
||||
}: RequestParams): Promise<{ body: GetAllStatsRuleMigrationResponse }> => {
|
||||
const response = await supertest
|
||||
.get(SIEM_RULE_MIGRATIONS_ALL_STATS_PATH)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set(ELASTIC_HTTP_VERSION_HEADER, API_VERSIONS.internal.v1)
|
||||
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
||||
.send();
|
||||
|
||||
assertStatusCode(expectStatusCode, response);
|
||||
|
||||
return response;
|
||||
},
|
||||
|
||||
translationStats: async ({
|
||||
migrationId,
|
||||
expectStatusCode = 200,
|
||||
}: MigrationRequestParams): Promise<{ body: GetRuleMigrationStatsResponse }> => {
|
||||
const response = await supertest
|
||||
.get(
|
||||
replaceParams(SIEM_RULE_MIGRATION_TRANSLATION_STATS_PATH, { migration_id: migrationId })
|
||||
)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set(ELASTIC_HTTP_VERSION_HEADER, API_VERSIONS.internal.v1)
|
||||
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
||||
.send();
|
||||
|
||||
assertStatusCode(expectStatusCode, response);
|
||||
|
||||
return response;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue