Skip to content

Commit

Permalink
[Security Solution] Show deprecated bulk endpoints in Upgrade Assista…
Browse files Browse the repository at this point in the history
…nt (elastic#207091)

**Partially addresses: elastic#193184

## Summary
We are going to remove our deprecated [bulk action
endpoints](elastic#193184) in v9.0.0.
They are already unavailable in `main`.

This PR makes deprecated bulk endpoints visible in Upgrade Assistant.
Also, it adds an upgrade guide to the documentation to help users
transition to supported endpoints.

⚠️ This PR temporarily adds the deprecated endpoints back to
`register_routes.ts`. This is needed to make merging changes easier.
I'll open up a follow up PR for `main` that will delete these endpoints
from `9.0`. Will do it once this PR is merged.

## Screenshots
**Deprecated endpoints visible in Upgrade Assistant table**
<img width="1276" alt="Scherm­afbeelding 2025-01-21 om 11 27 53"
src="https://github.com/user-attachments/assets/909c7a20-31d9-46bb-89ec-b409550074e4"
/>

**Clicking on a table item opens a flyout with more info**
<table>
  <tr>
    <td>
<img width="1270" alt="patch_update"
src="https://github.com/user-attachments/assets/8e99459d-1ea1-40c4-936c-23074c02cd6f"
/>
    </td>
    <td>
<img width="1270" alt="post_create"
src="https://github.com/user-attachments/assets/6e734e97-4cf4-4d96-9f8d-51efeb3977ad"
/>
    </td>
  </tr>
  <tr>
    <td>
<img width="1270" alt="put_update"
src="https://github.com/user-attachments/assets/d5b08e16-bf49-475d-81a9-fe5654483e1d"
/>
    </td>
    <td>
<img width="1271" alt="post_delete"
src="https://github.com/user-attachments/assets/ccb74552-50a4-4bdb-b04e-06857caa8f38"
/>
    </td>
  </tr>
</table>

**Clicking on "Learn more" in the flyout takes you to the [upgrade
guide](https://kibana_bk_207091.docs-preview.app.elstc.co/guide/en/kibana/master/breaking-changes-summary.html#breaking-207091)**
<img width="731" alt="upgrade_notes"
src="https://github.com/user-attachments/assets/b8b471ea-98c7-4b07-91db-b1630c382554"
/>

## Testing
Once you send a request to one of the deprecated endpoints it should
show up in Upgrade Assistant at
`<basePath>/app/management/stack/upgrade_assistant/kibana_deprecations`.
Please refer to [this
ticket](elastic#193184) for the list of
deprecated endpoints.

Work started on: 16-Jan-2025
  • Loading branch information
nikitaindik committed Jan 22, 2025
1 parent 6bb93b1 commit d12e470
Show file tree
Hide file tree
Showing 24 changed files with 262 additions and 124 deletions.
36 changes: 36 additions & 0 deletions docs/upgrade-notes.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,42 @@ For Elastic Security release information, refer to {security-guide}/release-note
[float]
==== Kibana APIs

[discrete]
[[breaking-207091]]
.Removed legacy security rules bulk endpoints (9.0.0)
[%collapsible]
====
*Details* +
--
* `POST /api/detection_engine/rules/_bulk_create` has been replaced by `POST /api/detection_engine/rules/_import`
* `PUT /api/detection_engine/rules/_bulk_update` has been replaced by `POST /api/detection_engine/rules/_bulk_action`
* `PATCH /api/detection_engine/rules/_bulk_update has been replaced by `POST /api/detection_engine/rules/_bulk_action`
* `DELETE /api/detection_engine/rules/_bulk_delete` has been replaced by `POST /api/detection_engine/rules/_bulk_action`
* `POST api/detection_engine/rules/_bulk_delete` has been replaced by `POST /api/detection_engine/rules/_bulk_action`
--
These changes were introduced in {kibana-pull}197422[#197422].
*Impact* +
Deprecated endpoints will fail with a 404 status code starting from version 9.0.0
*Action* +
--
Update your implementations to use the new endpoints:
* **For bulk creation of rules:**
- Use `POST /api/detection_engine/rules/_import` (link:{api-kibana}/operation/operation-importrules[API documentation]) to create multiple rules along with their associated entities (for example, exceptions and action connectors).
- Alternatively, create rules individually using `POST /api/detection_engine/rules` (link:{api-kibana}/operation/operation-createrule[API documentation]).
* **For bulk updates of rules:**
- Use `POST /api/detection_engine/rules/_bulk_action` (link:{api-kibana}/operation/operation-performrulesbulkaction[API documentation]) to update fields in multiple rules simultaneously.
- Alternatively, update rules individually using `PUT /api/detection_engine/rules` (link:{api-kibana}/operation/operation-updaterule[API documentation]).
* **For bulk deletion of rules:**
- Use `POST /api/detection_engine/rules/_bulk_action` (link:{api-kibana}/operation/operation-performrulesbulkaction[API documentation]) to delete multiple rules by IDs or query.
- Alternatively, delete rules individually using `DELETE /api/detection_engine/rules` (link:{api-kibana}/operation/operation-deleterule[API documentation]).
--
====

[discrete]
[[breaking-199598]]
.Remove deprecated endpoint management endpoints (9.0.0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D
aiAssistant: `${SECURITY_SOLUTION_DOCS}security-assistant.html`,
signalsMigrationApi: `${SECURITY_SOLUTION_DOCS}signals-migration-api.html`,
legacyEndpointManagementApiDeprecations: `${KIBANA_DOCS}breaking-changes-summary.html#breaking-199598`,
legacyBulkApiDeprecations: `${KIBANA_DOCS}breaking-changes-summary.html#breaking-207091`,
},
query: {
eql: `${ELASTICSEARCH_DOCS}eql.html`,
Expand Down
1 change: 1 addition & 0 deletions src/platform/packages/shared/kbn-doc-links/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ export interface DocLinks {
readonly detectionEngineOverview: string;
readonly signalsMigrationApi: string;
readonly legacyEndpointManagementApiDeprecations: string;
readonly legacyBulkApiDeprecations: string;
};
readonly query: {
readonly eql: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ export const DETECTION_ENGINE_RULES_BULK_CREATE =
`${DETECTION_ENGINE_RULES_URL}/_bulk_create` as const;
export const DETECTION_ENGINE_RULES_BULK_UPDATE =
`${DETECTION_ENGINE_RULES_URL}/_bulk_update` as const;
export const DETECTION_ENGINE_RULES_IMPORT_URL = `${DETECTION_ENGINE_RULES_URL}/_import` as const;

export * from './entity_analytics/constants';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
import type { BulkActionsDryRunErrCode } from '../../../../common/constants';
import {
DETECTION_ENGINE_RULES_BULK_ACTION,
DETECTION_ENGINE_RULES_IMPORT_URL,
DETECTION_ENGINE_RULES_PREVIEW,
DETECTION_ENGINE_RULES_URL,
DETECTION_ENGINE_RULES_URL_FIND,
Expand Down Expand Up @@ -455,21 +456,18 @@ export const importRules = async ({
const formData = new FormData();
formData.append('file', fileToImport);

return KibanaServices.get().http.fetch<ImportDataResponse>(
`${DETECTION_ENGINE_RULES_URL}/_import`,
{
method: 'POST',
version: '2023-10-31',
headers: { 'Content-Type': undefined },
query: {
overwrite,
overwrite_exceptions: overwriteExceptions,
overwrite_action_connectors: overwriteActionConnectors,
},
body: formData,
signal,
}
);
return KibanaServices.get().http.fetch<ImportDataResponse>(DETECTION_ENGINE_RULES_IMPORT_URL, {
method: 'POST',
version: '2023-10-31',
headers: { 'Content-Type': undefined },
query: {
overwrite,
overwrite_exceptions: overwriteExceptions,
overwrite_action_connectors: overwriteActionConnectors,
},
body: formData,
signal,
});
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
DETECTION_ENGINE_RULES_BULK_DELETE,
DETECTION_ENGINE_RULES_BULK_CREATE,
DETECTION_ENGINE_RULES_URL_FIND,
DETECTION_ENGINE_RULES_IMPORT_URL,
} from '../../../../../common/constants';
import { RULE_MANAGEMENT_FILTERS_URL } from '../../../../../common/api/detection_engine/rule_management/urls';

Expand Down Expand Up @@ -260,14 +261,14 @@ export const getFindResultWithMultiHits = ({
export const getImportRulesRequest = (hapiStream?: HapiReadableStream) =>
requestMock.create({
method: 'post',
path: `${DETECTION_ENGINE_RULES_URL}/_import`,
path: DETECTION_ENGINE_RULES_IMPORT_URL,
body: { file: hapiStream },
});

export const getImportRulesRequestOverwriteTrue = (hapiStream?: HapiReadableStream) =>
requestMock.create({
method: 'post',
path: `${DETECTION_ENGINE_RULES_URL}/_import`,
path: DETECTION_ENGINE_RULES_IMPORT_URL,
body: { file: hapiStream },
query: { overwrite: true },
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import type { Logger } from '@kbn/core/server';
import type { DocLinksServiceSetup, Logger } from '@kbn/core/server';
import type { ConfigType } from '../../../../config';
import type { SetupPlugins } from '../../../../plugin_contract';
import type { SecuritySolutionPluginRouter } from '../../../../types';
Expand All @@ -26,12 +26,17 @@ import { readRuleRoute } from './rules/read_rule/route';
import { updateRuleRoute } from './rules/update_rule/route';
import { readTagsRoute } from './tags/read_tags/route';
import { getCoverageOverviewRoute } from './rules/coverage_overview/route';
import { bulkCreateRulesRoute } from './rules/bulk_create_rules/route';
import { bulkUpdateRulesRoute } from './rules/bulk_update_rules/route';
import { bulkPatchRulesRoute } from './rules/bulk_patch_rules/route';
import { bulkDeleteRulesRoute } from './rules/bulk_delete_rules/route';

export const registerRuleManagementRoutes = (
router: SecuritySolutionPluginRouter,
config: ConfigType,
ml: SetupPlugins['ml'],
logger: Logger
logger: Logger,
docLinks: DocLinksServiceSetup
) => {
// Rules CRUD
createRuleRoute(router);
Expand All @@ -40,11 +45,11 @@ export const registerRuleManagementRoutes = (
patchRuleRoute(router);
deleteRuleRoute(router);

// Rules bulk CRUD
bulkCreateRulesRoute(router, logger);
bulkUpdateRulesRoute(router, logger);
bulkPatchRulesRoute(router, logger);
bulkDeleteRulesRoute(router, logger);
// These four bulk endpoints are deprecated and will be removed in 9.0
bulkCreateRulesRoute(router, logger, docLinks);
bulkUpdateRulesRoute(router, logger, docLinks);
bulkPatchRulesRoute(router, logger, docLinks);
bulkDeleteRulesRoute(router, logger, docLinks);

// Rules bulk actions
performBulkActionRoute(router, config, ml, logger);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { bulkCreateRulesRoute } from './route';
import { getCreateRulesSchemaMock } from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks';
import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks';
import { getQueryRuleParams } from '../../../../rule_schema/mocks';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { loggingSystemMock, docLinksServiceMock } from '@kbn/core/server/mocks';
import { HttpAuthzError } from '../../../../../machine_learning/validation';
import { getRulesSchemaMock } from '../../../../../../../common/api/detection_engine/model/rule_schema/rule_response_schema.mock';

Expand All @@ -32,14 +32,15 @@ describe('Bulk create rules route', () => {
server = serverMock.create();
({ clients, context } = requestContextMock.createTools());
const logger = loggingSystemMock.createLogger();
const docLinks = docLinksServiceMock.createSetupContract();

clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no existing rules
clients.rulesClient.create.mockResolvedValue(getRuleMock(getQueryRuleParams())); // successful creation
clients.detectionRulesClient.createCustomRule.mockResolvedValue(getRulesSchemaMock());
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse())
);
bulkCreateRulesRoute(server.router, logger);
bulkCreateRulesRoute(server.router, logger, docLinks);
});

describe('status codes', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
* 2.0.
*/

import type { IKibanaResponse, Logger } from '@kbn/core/server';
import type { DocLinksServiceSetup, IKibanaResponse, Logger } from '@kbn/core/server';

import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import { DETECTION_ENGINE_RULES_BULK_CREATE } from '../../../../../../../common/constants';
import {
DETECTION_ENGINE_RULES_BULK_CREATE,
DETECTION_ENGINE_RULES_IMPORT_URL,
} from '../../../../../../../common/constants';
import {
BulkCreateRulesRequestBody,
validateCreateRuleProps,
Expand All @@ -32,7 +35,11 @@ import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from '../.
/**
* @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead
*/
export const bulkCreateRulesRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => {
export const bulkCreateRulesRoute = (
router: SecuritySolutionPluginRouter,
logger: Logger,
docLinks: DocLinksServiceSetup
) => {
router.versioned
.post({
access: 'public',
Expand All @@ -56,6 +63,17 @@ export const bulkCreateRulesRoute = (router: SecuritySolutionPluginRouter, logge
body: buildRouteValidationWithZod(BulkCreateRulesRequestBody),
},
},
options: {
deprecated: {
documentationUrl: docLinks.links.securitySolution.legacyBulkApiDeprecations,
severity: 'critical',
reason: {
type: 'migrate',
newApiMethod: 'POST',
newApiPath: DETECTION_ENGINE_RULES_IMPORT_URL,
},
},
},
},
async (context, request, response): Promise<IKibanaResponse<BulkCrudRulesResponse>> => {
logDeprecatedBulkEndpoint(logger, DETECTION_ENGINE_RULES_BULK_CREATE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from '../../../../routes/__mocks__/request_responses';
import { requestContextMock, serverMock, requestMock } from '../../../../routes/__mocks__';
import { bulkDeleteRulesRoute } from './route';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { loggingSystemMock, docLinksServiceMock } from '@kbn/core/server/mocks';

describe('Bulk delete rules route', () => {
let server: ReturnType<typeof serverMock.create>;
Expand All @@ -27,12 +27,13 @@ describe('Bulk delete rules route', () => {
server = serverMock.create();
({ clients, context } = requestContextMock.createTools());
const logger = loggingSystemMock.createLogger();
const docLinks = docLinksServiceMock.createSetupContract();

clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists
clients.rulesClient.delete.mockResolvedValue({}); // successful deletion
clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); // rule status request

bulkDeleteRulesRoute(server.router, logger);
bulkDeleteRulesRoute(server.router, logger, docLinks);
});

describe('status codes with actionClient and alertClient', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
*/

import type { VersionedRouteConfig } from '@kbn/core-http-server';
import type { IKibanaResponse, Logger, RequestHandler } from '@kbn/core/server';
import type {
DocLinksServiceSetup,
IKibanaResponse,
Logger,
RequestHandler,
} from '@kbn/core/server';
import { transformError } from '@kbn/securitysolution-es-utils';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type {
Expand All @@ -19,7 +24,10 @@ import {
BulkDeleteRulesRequestBody,
validateQueryRuleByIds,
} from '../../../../../../../common/api/detection_engine/rule_management';
import { DETECTION_ENGINE_RULES_BULK_DELETE } from '../../../../../../../common/constants';
import {
DETECTION_ENGINE_RULES_BULK_ACTION,
DETECTION_ENGINE_RULES_BULK_DELETE,
} from '../../../../../../../common/constants';
import type {
SecuritySolutionPluginRouter,
SecuritySolutionRequestHandlerContext,
Expand All @@ -46,7 +54,11 @@ type Handler = RequestHandler<
/**
* @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead
*/
export const bulkDeleteRulesRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => {
export const bulkDeleteRulesRoute = (
router: SecuritySolutionPluginRouter,
logger: Logger,
docLinks: DocLinksServiceSetup
) => {
const handler: Handler = async (
context,
request,
Expand Down Expand Up @@ -124,6 +136,17 @@ export const bulkDeleteRulesRoute = (router: SecuritySolutionPluginRouter, logge
body: buildRouteValidationWithZod(BulkDeleteRulesRequestBody),
},
},
options: {
deprecated: {
documentationUrl: docLinks.links.securitySolution.legacyBulkApiDeprecations,
severity: 'critical',
reason: {
type: 'migrate',
newApiMethod: 'POST',
newApiPath: DETECTION_ENGINE_RULES_BULK_ACTION,
},
},
},
},
handler
);
Expand All @@ -135,6 +158,17 @@ export const bulkDeleteRulesRoute = (router: SecuritySolutionPluginRouter, logge
body: buildRouteValidationWithZod(BulkDeleteRulesPostRequestBody),
},
},
options: {
deprecated: {
documentationUrl: docLinks.links.securitySolution.legacyBulkApiDeprecations,
severity: 'critical',
reason: {
type: 'migrate',
newApiMethod: 'POST',
newApiPath: DETECTION_ENGINE_RULES_BULK_ACTION,
},
},
},
},
handler
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
import { bulkPatchRulesRoute } from './route';
import { getCreateRulesSchemaMock } from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks';
import { getMlRuleParams, getQueryRuleParams } from '../../../../rule_schema/mocks';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { loggingSystemMock, docLinksServiceMock } from '@kbn/core/server/mocks';
import { HttpAuthzError } from '../../../../../machine_learning/validation';

describe('Bulk patch rules route', () => {
Expand All @@ -35,12 +35,13 @@ describe('Bulk patch rules route', () => {
server = serverMock.create();
({ clients, context } = requestContextMock.createTools());
const logger = loggingSystemMock.createLogger();
const docLinks = docLinksServiceMock.createSetupContract();

clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists
clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); // update succeeds
clients.detectionRulesClient.patchRule.mockResolvedValue(getRulesSchemaMock());

bulkPatchRulesRoute(server.router, logger);
bulkPatchRulesRoute(server.router, logger, docLinks);
});

describe('status codes', () => {
Expand Down
Loading

0 comments on commit d12e470

Please sign in to comment.