From c937e95e3137821b510fa480ee28f0cf3afb85ad Mon Sep 17 00:00:00 2001 From: Ievgen Sorokopud Date: Fri, 13 Sep 2024 10:37:39 +0200 Subject: [PATCH 01/17] [Security Solution] Add validation error description on prebuilt rule editing (#191832) (#192683) ## Summary Partially addressed https://github.com/elastic/kibana/issues/191832 With these changes: - We revert to the https://github.com/elastic/kibana/issues/180407#issuecomment-2312891214. Specifically, we return back the validation errors to the modal window. An example of this modal is in the ticket description. - Additionally, on the Rule Editing page and **only for prebuilt rules** we: 1) hide the callout that says "You have an invalid input in this tab: ...", and 2) we don't show the modal if there are any data validation errors. We shouldn't show this modal and this callout until we release the prebuilt rule customization feature. 3) We will only validate the Actions tab. - Fix MKI flaky cypress tests introduced in https://github.com/elastic/kibana/pull/191487 ([1](https://buildkite.com/organizations/elastic/analytics/suites/serverless-mki-cypress-detection-engine/tests/b1f442af-db44-8029-a9fb-7e3d988303b3?branch=main), [2](https://buildkite.com/organizations/elastic/analytics/suites/serverless-mki-cypress-detection-engine/tests/995655b6-ae70-86fd-b483-c65846cd8d66?branch=main), [3](https://buildkite.com/organizations/elastic/analytics/suites/serverless-mki-cypress-detection-engine/tests/02318f5c-6ca1-8779-a5a4-60f52a55b344?branch=main)). All three tests are failing due to missing `[data-test-subj="eqlRuleType"]` element. After checking and comparing my tests to other similar tests in the file, the only difference that I've found was extra `login();` call. Thus removing those. Here is the screen recording showing the new behaviour for prebuilt rules. The has missing data source query validation error, though we do not show it and allow user just to save the rule. Only Actions tab is validated on rule save action. https://github.com/user-attachments/assets/ce968f51-1a53-41b2-ad06-1b31dec085a6 ### Checklist Delete any items that are not applicable to this PR. - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed * [Detection Engine - Cypress](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6925) (100 ESS & 100 Serverless) * [Rule Management - Cypress](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6926) (100 ESS & 100 Serverless) * [Prebuilt Rules - Cypress](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6927) (100 ESS & 100 Serverless) --- .../save_with_errors_confirmation/index.tsx | 16 ++++++++++++++-- .../pages/rule_editing/index.tsx | 14 ++++++++++++-- .../rule_creation/eql_rule.cy.ts | 3 --- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/save_with_errors_confirmation/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/save_with_errors_confirmation/index.tsx index 839513bf0e34c..3f14945bedadc 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/save_with_errors_confirmation/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/save_with_errors_confirmation/index.tsx @@ -7,7 +7,7 @@ import React from 'react'; -import { EuiConfirmModal } from '@elastic/eui'; +import { EuiConfirmModal, EuiSpacer, EuiText } from '@elastic/eui'; import * as i18n from './translations'; @@ -32,7 +32,19 @@ const SaveWithErrorsModalComponent = ({ confirmButtonText={i18n.SAVE_WITH_ERRORS_CONFIRM_BUTTON} defaultFocusedButton="confirm" > - <>{i18n.SAVE_WITH_ERRORS_MODAL_MESSAGE(errors.length)} + <> + {i18n.SAVE_WITH_ERRORS_MODAL_MESSAGE(errors.length)} + + + ); }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx index 1ecbde5b00d7b..6c139acbbdbff 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx @@ -436,11 +436,20 @@ const EditRulePageComponent: FC<{ rule: RuleResponse }> = ({ rule }) => { const onSubmit = useCallback(async () => { setNonBlockingRuleErrors([]); + const actionsStepFormValid = await actionsStepForm.validate(); + if (rule.immutable) { + // Since users cannot edit Define, About and Schedule tabs of the rule, we skip validation of those to avoid + // user confusion of seeing that those tabs have error and not being able to see or do anything about that. + // We will need to remove this condition once rule customization work is done. + if (actionsStepFormValid) { + await saveChanges(); + } + return; + } + const defineStepFormValid = await defineStepForm.validate(); const aboutStepFormValid = await aboutStepForm.validate(); const scheduleStepFormValid = await scheduleStepForm.validate(); - const actionsStepFormValid = await actionsStepForm.validate(); - if ( defineStepFormValid && aboutStepFormValid && @@ -465,6 +474,7 @@ const EditRulePageComponent: FC<{ rule: RuleResponse }> = ({ rule }) => { showSaveWithErrorsModal(); } }, [ + rule.immutable, defineStepForm, aboutStepForm, scheduleStepForm, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts index a14218fcdda56..dea2e554e8e4f 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/eql_rule.cy.ts @@ -231,7 +231,6 @@ describe('EQL rules', { tags: ['@ess', '@serverless'] }, () => { const rule = getEqlRule(); it('validates missing data source', () => { - login(); visit(CREATE_RULE_URL); selectEqlRuleType(); getIndexPatternClearButton().click(); @@ -258,7 +257,6 @@ describe('EQL rules', { tags: ['@ess', '@serverless'] }, () => { }); it('validates missing data fields', () => { - login(); visit(CREATE_RULE_URL); selectEqlRuleType(); @@ -282,7 +280,6 @@ describe('EQL rules', { tags: ['@ess', '@serverless'] }, () => { }); it('validates syntax errors', () => { - login(); visit(CREATE_RULE_URL); selectEqlRuleType(); From b52695edfb15e555eedbe0be1ece4a779ddfbd97 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 13 Sep 2024 10:55:36 +0200 Subject: [PATCH 02/17] [Cloud] Add configuration for CSP value (#190491) ## Summary Exposes the CSP from the cloud setup plugin contract. Related https://github.com/elastic/kibana/issues/190023 ## Notes Intended for cloud deployments, how/where will this config value be set? ### Checklist Delete any items that are not applicable to this PR. - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../test_suites/core_plugins/rendering.ts | 1 + x-pack/plugins/cloud/public/plugin.test.ts | 6 ++++++ x-pack/plugins/cloud/public/plugin.tsx | 3 +++ x-pack/plugins/cloud/public/types.ts | 6 ++++++ .../plugins/cloud/server/__snapshots__/plugin.test.ts.snap | 1 + x-pack/plugins/cloud/server/config.ts | 2 ++ x-pack/plugins/cloud/server/plugin.test.ts | 6 ++++++ x-pack/plugins/cloud/server/plugin.ts | 7 +++++++ 8 files changed, 32 insertions(+) diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index 95a4b8899eb18..9bd43504d7db1 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -229,6 +229,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.ccr.ui.enabled (boolean?)', 'xpack.cloud.base_url (string?)', 'xpack.cloud.cname (string?)', + 'xpack.cloud.csp (string?)', 'xpack.cloud.deployment_url (string?)', 'xpack.cloud.deployments_url (string?)', 'xpack.cloud.is_elastic_staff_owned (boolean?)', diff --git a/x-pack/plugins/cloud/public/plugin.test.ts b/x-pack/plugins/cloud/public/plugin.test.ts index 61ed450324637..2c32ac8fe972a 100644 --- a/x-pack/plugins/cloud/public/plugin.test.ts +++ b/x-pack/plugins/cloud/public/plugin.test.ts @@ -31,6 +31,7 @@ describe('Cloud Plugin', () => { ...baseConfig, id: 'cloudId', cname: 'cloud.elastic.co', + csp: 'aws', ...configParts, }); const plugin = new CloudPlugin(initContext); @@ -86,6 +87,11 @@ describe('Cloud Plugin', () => { expect(setup.cname).toBe('cloud.elastic.co'); }); + it('exposes csp', () => { + const { setup } = setupPlugin(); + expect(setup.csp).toBe('aws'); + }); + it('exposes registerCloudService', () => { const { setup } = setupPlugin(); expect(setup.registerCloudService).toBeDefined(); diff --git a/x-pack/plugins/cloud/public/plugin.tsx b/x-pack/plugins/cloud/public/plugin.tsx index d2671b18e4d68..a661933955060 100644 --- a/x-pack/plugins/cloud/public/plugin.tsx +++ b/x-pack/plugins/cloud/public/plugin.tsx @@ -23,6 +23,7 @@ export interface CloudConfigType { id?: string; organization_id?: string; cname?: string; + csp?: string; base_url?: string; profile_url?: string; deployments_url?: string; @@ -82,6 +83,7 @@ export class CloudPlugin implements Plugin { base_url: baseUrl, trial_end_date: trialEndDate, is_elastic_staff_owned: isElasticStaffOwned, + csp, } = this.config; let decodedId: DecodedCloudId | undefined; @@ -94,6 +96,7 @@ export class CloudPlugin implements Plugin { organizationId: this.config.organization_id, deploymentId: parseDeploymentIdFromDeploymentUrl(this.config.deployment_url), cname, + csp, baseUrl, ...this.getCloudUrls(), elasticsearchUrl: decodedId?.elasticsearchUrl, diff --git a/x-pack/plugins/cloud/public/types.ts b/x-pack/plugins/cloud/public/types.ts index dd3dcf27c1a61..8df1ba645cb48 100644 --- a/x-pack/plugins/cloud/public/types.ts +++ b/x-pack/plugins/cloud/public/types.ts @@ -111,6 +111,12 @@ export interface CloudSetup { * @example `cloud.elastic.co` */ cname?: string; + /** + * The cloud service provider identifier. + * + * @note Expected to be one of `aws`, `gcp` or `azure`, but could be something different. + */ + csp?: string; /** * This is the URL of the Cloud interface. */ diff --git a/x-pack/plugins/cloud/server/__snapshots__/plugin.test.ts.snap b/x-pack/plugins/cloud/server/__snapshots__/plugin.test.ts.snap index fa873a89a85d7..0f79db1ef779b 100644 --- a/x-pack/plugins/cloud/server/__snapshots__/plugin.test.ts.snap +++ b/x-pack/plugins/cloud/server/__snapshots__/plugin.test.ts.snap @@ -10,6 +10,7 @@ Object { "cloudDefaultPort": undefined, "cloudHost": undefined, "cloudId": "cloudId", + "csp": "aws", "deploymentId": "deployment-id", "elasticsearchUrl": undefined, "instanceSizeMb": undefined, diff --git a/x-pack/plugins/cloud/server/config.ts b/x-pack/plugins/cloud/server/config.ts index 371f895b92e09..ddf9b9039645e 100644 --- a/x-pack/plugins/cloud/server/config.ts +++ b/x-pack/plugins/cloud/server/config.ts @@ -22,6 +22,7 @@ const configSchema = schema.object({ apm: schema.maybe(apmConfigSchema), base_url: schema.maybe(schema.string()), cname: schema.maybe(schema.string()), + csp: schema.maybe(schema.string()), deployments_url: schema.string({ defaultValue: '/deployments' }), deployment_url: schema.maybe(schema.string()), id: schema.maybe(schema.string()), @@ -59,6 +60,7 @@ export const config: PluginConfigDescriptor = { exposeToBrowser: { base_url: true, cname: true, + csp: true, deployments_url: true, deployment_url: true, id: true, diff --git a/x-pack/plugins/cloud/server/plugin.test.ts b/x-pack/plugins/cloud/server/plugin.test.ts index f2ef8375b417e..598d03dd96256 100644 --- a/x-pack/plugins/cloud/server/plugin.test.ts +++ b/x-pack/plugins/cloud/server/plugin.test.ts @@ -30,6 +30,7 @@ describe('Cloud Plugin', () => { ...baseConfig, id: 'cloudId', cname: 'cloud.elastic.co', + csp: 'aws', ...configParts, }); const plugin = new CloudPlugin(initContext); @@ -77,6 +78,11 @@ describe('Cloud Plugin', () => { ); }); + it('exposes csp', () => { + const { setup } = setupPlugin(); + expect(setup.csp).toBe('aws'); + }); + it('exposes components decoded from the cloudId', () => { const decodedId: DecodedCloudId = { defaultPort: '9000', diff --git a/x-pack/plugins/cloud/server/plugin.ts b/x-pack/plugins/cloud/server/plugin.ts index 4d77cae8d870f..a03878b760dd4 100644 --- a/x-pack/plugins/cloud/server/plugin.ts +++ b/x-pack/plugins/cloud/server/plugin.ts @@ -35,6 +35,12 @@ export interface CloudSetup { * @note The `cloudId` is a concatenation of the deployment name and a hash. Users can update the deployment name, changing the `cloudId`. However, the changed `cloudId` will not be re-injected into `kibana.yml`. If you need the current `cloudId` the best approach is to split the injected `cloudId` on the semi-colon, and replace the first element with the `persistent.cluster.metadata.display_name` value as provided by a call to `GET _cluster/settings`. */ cloudId?: string; + /** + * The cloud service provider identifier. + * + * @note Expected to be one of `aws`, `gcp` or `azure`, but could be something different. + */ + csp?: string; /** * The Elastic Cloud Organization that owns this deployment/project. */ @@ -199,6 +205,7 @@ export class CloudPlugin implements Plugin { return { ...this.getCloudUrls(), cloudId: this.config.id, + csp: this.config.csp, organizationId, instanceSizeMb: readInstanceSizeMb(), deploymentId, From 8ce63ab2026ca1de94a5d26e948b53a2bb641d22 Mon Sep 17 00:00:00 2001 From: Paul Bianciardi <70908889+paulb-elastic@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:56:01 +0100 Subject: [PATCH 03/17] Update the Uptime deprecation and removal details (#192707) ## Summary There has been an updated decision not to remove the Uptime app from 9.0.0, but will be done in a future version, in early 2026. The wording of the deprecation/removal notice in the 8.15.0 release note has been updated to reflect this. ### Checklist - [X] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing) --- docs/CHANGELOG.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc index d12d5a91f8273..c164708bc12d8 100644 --- a/docs/CHANGELOG.asciidoc +++ b/docs/CHANGELOG.asciidoc @@ -179,11 +179,11 @@ you make the necessary updates after you upgrade to 8.15.0. [discrete] [[deprecation-uptime]] -.Uptime is deprecated in 8.15.0 and will be removed in 9.0.0. +.Uptime is deprecated in 8.15.0 and will be removed in a future version. [%collapsible] ==== *Details* + -The Uptime app is already hidden from Kibana when there is no recent Heartbeat data, but will be completely removed in 9.0.0. You should migrate to Synthetics as an alternative. For more details, refer to the {observability-guide}/uptime-intro.html[Uptime documentation]. +The Uptime app is already hidden from Kibana when there is no recent Heartbeat data, and will be completely removed in early 2026. You should migrate to Synthetics as an alternative. For more details, refer to the {observability-guide}/uptime-intro.html[Uptime documentation]. ==== [float] From 9c18dfad14b684968263977e27bb59a989444aa1 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 13 Sep 2024 11:08:21 +0200 Subject: [PATCH 04/17] [Chore][OAS] Remove use of `any` in generator lib utility (#192440) ## Summary Tiny change, using a cast to `Record` instead of relying on `any`. --- .../post_process_mutations/mutations/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/post_process_mutations/mutations/utils.ts b/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/post_process_mutations/mutations/utils.ts index c2b17f05dacd4..abce6d470a35d 100644 --- a/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/post_process_mutations/mutations/utils.ts +++ b/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/post_process_mutations/mutations/utils.ts @@ -43,8 +43,8 @@ export const processDiscontinued = (schema: OpenAPIV3.SchemaObject): void => { }; /** Just for type convenience */ -export const deleteField = (schema: Record, field: string): void => { - delete schema[field]; +export const deleteField = (schema: object, field: string): void => { + delete (schema as Record)[field]; }; export const isAnyType = (schema: OpenAPIV3.SchemaObject): boolean => { From c304b34e0edd90dedcb67dff10da6472d4a823c0 Mon Sep 17 00:00:00 2001 From: Maryam Saeidi Date: Fri, 13 Sep 2024 11:10:15 +0200 Subject: [PATCH 05/17] [Metric threshold] Fix the condition not showing up in the metric threshold flyout (#192736) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #192439 ## Summary This PR reverts this [line](https://github.com/elastic/kibana/pull/191948/files#diff-2dd82a791bba3d995e9e6b35d4a973053f166351cc6025a5cd1d24dc789766aeR48) in a previous [PR](https://github.com/elastic/kibana/pull/191948) that caused an issue in loading the metric threshold flyout on the observability alerts page. | Before ❌ | After ✅ | |---|---| |![Image](https://github.com/user-attachments/assets/3c0b8812-8cd9-4769-bd20-ab10f559009b)|![Image](https://github.com/user-attachments/assets/9823e691-ce18-4c00-8748-ce5797a19943)| I also added a small test that fails before this fix. --- .../components/expression_row.tsx | 2 +- .../containers/metrics_source/source.tsx | 2 +- .../observability/alerts/rules_page.ts | 15 ++++++++ .../apps/observability/index.ts | 1 + .../pages/alerts/metric_threshold.ts | 38 +++++++++++++++++++ 5 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 x-pack/test/observability_functional/apps/observability/pages/alerts/metric_threshold.ts diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx index 48371bebb2569..8aeb7e19402fb 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx @@ -200,7 +200,7 @@ export const ExpressionRow = ({ return ( <> - + { method: 'GET', } ); - telemetry.reportPerformanceMetricEvent( + telemetry?.reportPerformanceMetricEvent( 'infra_source_load', performance.now() - start, {}, diff --git a/x-pack/test/functional/services/observability/alerts/rules_page.ts b/x-pack/test/functional/services/observability/alerts/rules_page.ts index 226d257ed918b..76f700f99b999 100644 --- a/x-pack/test/functional/services/observability/alerts/rules_page.ts +++ b/x-pack/test/functional/services/observability/alerts/rules_page.ts @@ -6,6 +6,8 @@ */ import { FtrProviderContext } from '../../../ftr_provider_context'; +const METRIC_THRESHOLD_RULE_TYPE_SELECTOR = 'metrics.alert.threshold-SelectOption'; + export function ObservabilityAlertsRulesProvider({ getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const find = getService('find'); @@ -30,6 +32,17 @@ export function ObservabilityAlertsRulesProvider({ getService }: FtrProviderCont await find.clickByButtonText('metric-threshold'); }; + const clickOnInfrastructureCategory = async () => { + const categories = await testSubjects.find('ruleTypeModal'); + const category = await categories.findByCssSelector(`.euiFacetButton[title="Infrastructure"]`); + await category.click(); + }; + + const clickOnMetricThresholdRule = async () => { + await testSubjects.existOrFail(METRIC_THRESHOLD_RULE_TYPE_SELECTOR); + await testSubjects.click(METRIC_THRESHOLD_RULE_TYPE_SELECTOR); + }; + return { getManageRulesPageHref, clickCreateRuleButton, @@ -37,5 +50,7 @@ export function ObservabilityAlertsRulesProvider({ getService }: FtrProviderCont clickDisableFromDropDownMenu, clickLogsTab, clickOnRuleInEventLogs, + clickOnInfrastructureCategory, + clickOnMetricThresholdRule, }; } diff --git a/x-pack/test/observability_functional/apps/observability/index.ts b/x-pack/test/observability_functional/apps/observability/index.ts index 67c00ef846a2c..64636e79123d7 100644 --- a/x-pack/test/observability_functional/apps/observability/index.ts +++ b/x-pack/test/observability_functional/apps/observability/index.ts @@ -24,5 +24,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./pages/rules_page')); loadTestFile(require.resolve('./pages/rule_details_page')); loadTestFile(require.resolve('./pages/alert_details_page')); + loadTestFile(require.resolve('./pages/alerts/metric_threshold')); }); } diff --git a/x-pack/test/observability_functional/apps/observability/pages/alerts/metric_threshold.ts b/x-pack/test/observability_functional/apps/observability/pages/alerts/metric_threshold.ts new file mode 100644 index 0000000000000..e474314b72178 --- /dev/null +++ b/x-pack/test/observability_functional/apps/observability/pages/alerts/metric_threshold.ts @@ -0,0 +1,38 @@ +/* + * 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 { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const testSubjects = getService('testSubjects'); + + describe('Metric threshold rule', function () { + this.tags('includeFirefox'); + + const observability = getService('observability'); + + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); + await observability.alerts.common.navigateToRulesPage(); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs'); + }); + + it('shows the metric threshold rule in the observability section', async () => { + await observability.alerts.rulesPage.clickCreateRuleButton(); + await observability.alerts.rulesPage.clickOnInfrastructureCategory(); + await observability.alerts.rulesPage.clickOnMetricThresholdRule(); + }); + + it('shows an expression row in the condition section', async () => { + await testSubjects.existOrFail('metricThresholdExpressionRow'); + }); + }); +}; From a94a4db8bc9e9d923273beb5c31b2253172a8568 Mon Sep 17 00:00:00 2001 From: Dzmitry Lemechko Date: Fri, 13 Sep 2024 12:24:08 +0200 Subject: [PATCH 06/17] [ftr] enable mock-idp-plugin for stateful (deployment-agnostic) tests (#192279) ## Summary closes #190221 This PR enables `mock-idp-plugin` when Kibana is started with stateful FTR config for deployment-agnostic tests: ``` node scripts/functional_tests_server --config=x-pack/test/api_integration/deployment_agnostic/configs/stateful/platform.stateful.config.ts ``` image You can pick up one of the the role defined for stateful SAML authentication in https://github.com/elastic/kibana/blob/main/packages/kbn-es/src/stateful_resources/roles.yml Note: this plugin is only enabled locally for a better manual testing experience, it is **not loaded on CI** It is done to unify DevEx when folks work on deployment-agnostic tests and would like to confirm the functionality under the same role for both stateful and serverless deployments. Thanks @azasypkin for the help, again :) How to test: - start the servers using `x-pack/test/api_integration/deployment_agnostic/configs/stateful/platform.stateful.config.ts` config and go to `http://localhost:5620` - try to login with different roles, make sure valid role is applied in top right profile menu --- packages/kbn-mock-idp-plugin/server/index.ts | 1 + packages/kbn-mock-idp-plugin/server/plugin.ts | 37 +++++++++++-------- .../default_configs/stateful.config.base.ts | 18 +++++++++ 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/packages/kbn-mock-idp-plugin/server/index.ts b/packages/kbn-mock-idp-plugin/server/index.ts index 4fc152eaeaf31..04525e09ee310 100644 --- a/packages/kbn-mock-idp-plugin/server/index.ts +++ b/packages/kbn-mock-idp-plugin/server/index.ts @@ -17,6 +17,7 @@ export const config = { // The plugin should only be enabled in Serverless. enabled: offeringBasedSchema({ serverless: schema.boolean({ defaultValue: true }), + traditional: schema.boolean({ defaultValue: false }), options: { defaultValue: false }, }), }), diff --git a/packages/kbn-mock-idp-plugin/server/plugin.ts b/packages/kbn-mock-idp-plugin/server/plugin.ts index 82a416e390122..27d5b96a4cbfc 100644 --- a/packages/kbn-mock-idp-plugin/server/plugin.ts +++ b/packages/kbn-mock-idp-plugin/server/plugin.ts @@ -11,7 +11,11 @@ import type { PluginInitializer, Plugin } from '@kbn/core-plugins-server'; import { schema } from '@kbn/config-schema'; import type { TypeOf } from '@kbn/config-schema'; import { MOCK_IDP_LOGIN_PATH, MOCK_IDP_LOGOUT_PATH, createSAMLResponse } from '@kbn/mock-idp-utils'; -import { SERVERLESS_ROLES_ROOT_PATH, readRolesFromResource } from '@kbn/es'; +import { + SERVERLESS_ROLES_ROOT_PATH, + STATEFUL_ROLES_ROOT_PATH, + readRolesFromResource, +} from '@kbn/es'; import { resolve } from 'path'; import { CloudSetup } from '@kbn/cloud-plugin/server'; @@ -42,6 +46,11 @@ const readServerlessRoles = (projectType: string) => { } }; +const readStatefulRoles = () => { + const rolesResourcePath = resolve(STATEFUL_ROLES_ROOT_PATH, 'roles.yml'); + return readRolesFromResource(rolesResourcePath); +}; + export type CreateSAMLResponseParams = TypeOf; export const plugin: PluginInitializer< @@ -73,22 +82,18 @@ export const plugin: PluginInitializer< options: { authRequired: false }, }, (context, request, response) => { - const projectType = plugins.cloud.serverless?.projectType; - if (!projectType) { - return response.customError({ statusCode: 500, body: 'projectType is not defined' }); - } else { - try { - if (roles.length === 0) { - roles.push(...readServerlessRoles(projectType)); - } - return response.ok({ - body: { - roles, - }, - }); - } catch (err) { - return response.customError({ statusCode: 500, body: err.message }); + try { + if (roles.length === 0) { + const projectType = plugins.cloud?.serverless?.projectType; + roles.push(...(projectType ? readServerlessRoles(projectType) : readStatefulRoles())); } + return response.ok({ + body: { + roles, + }, + }); + } catch (err) { + return response.customError({ statusCode: 500, body: err.message }); } } ); diff --git a/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts b/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts index 964627effe28b..66a23b2e15bf8 100644 --- a/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts +++ b/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts @@ -43,12 +43,19 @@ export function createStatefulTestConfig Date: Fri, 13 Sep 2024 13:13:49 +0200 Subject: [PATCH 07/17] [HTTP/OAS] Improve RegEx performance of path variable matcher (#192687) ## Summary Minor improvement to the existing regexp and added some test coverage for the utility. Close https://github.com/elastic/kibana/issues/192586 ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../src/util.test.ts | 21 +++++++++++++++++++ .../kbn-router-to-openapispec/src/util.ts | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/kbn-router-to-openapispec/src/util.test.ts b/packages/kbn-router-to-openapispec/src/util.test.ts index c72cc746ec91c..79b4ddf8eba84 100644 --- a/packages/kbn-router-to-openapispec/src/util.test.ts +++ b/packages/kbn-router-to-openapispec/src/util.test.ts @@ -14,6 +14,7 @@ import { getXsrfHeaderForMethod, mergeResponseContent, prepareRoutes, + getPathParameters, } from './util'; import { assignToPaths, extractTags } from './util'; @@ -230,3 +231,23 @@ describe('getXsrfHeaderForMethod', () => { expect(getXsrfHeaderForMethod(method as RouteMethod, options)).toEqual(expected); }); }); + +describe('getPathParameters', () => { + test.each([ + ['', {}], + ['/', {}], + ['{}', {}], + ['{{}', {}], + ['{badinput', {}], + ['{ok}', { ok: { optional: false } }], + ['{ok?}', { ok: { optional: true } }], + ['{ok??}', {}], + ['/api/{path}/is/{cool}', { path: { optional: false }, cool: { optional: false } }], + [ + '/{required}/and/{optional?}', + { required: { optional: false }, optional: { optional: true } }, + ], + ])('%s', (input, output) => { + expect(getPathParameters(input)).toEqual(output); + }); +}); diff --git a/packages/kbn-router-to-openapispec/src/util.ts b/packages/kbn-router-to-openapispec/src/util.ts index c30d11fa27e15..1aa2a080ccc18 100644 --- a/packages/kbn-router-to-openapispec/src/util.ts +++ b/packages/kbn-router-to-openapispec/src/util.ts @@ -62,7 +62,7 @@ export const buildGlobalTags = (paths: OpenAPIV3.PathsObject, additionalTags: st }; export const getPathParameters = (path: string): KnownParameters => { - return Array.from(path.matchAll(/\{(.+?)\}/g)).reduce((acc, [_, key]) => { + return Array.from(path.matchAll(/\{([^{}?]+\??)\}/g)).reduce((acc, [_, key]) => { const optional = key.endsWith('?'); acc[optional ? key.slice(0, key.length - 1) : key] = { optional }; return acc; From e5f01344644a31f068a79279df24a36c264080af Mon Sep 17 00:00:00 2001 From: Khristinin Nikita Date: Fri, 13 Sep 2024 13:16:48 +0200 Subject: [PATCH 08/17] Use the same date for set alert timestmap (#192668) ## Summary Looks like [there can be](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6918#0191e0e0-8cde-48a7-a47e-d0919db0f220) situation when new Date() can produce slightly different result for those fields So, I just extract them into 1 variable --- .../utils/create_persistence_rule_type_wrapper.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts b/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts index 78043a961a1fd..854cebc9b0bf9 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_persistence_rule_type_wrapper.ts @@ -65,16 +65,18 @@ const augmentAlerts = ({ intendedTimestamp: Date | undefined; }) => { const commonRuleFields = getCommonAlertFields(options); + const currentDate = new Date(); + const timestampOverrideOrCurrent = currentTimeOverride ?? currentDate; return alerts.map((alert) => { return { ...alert, _source: { - [ALERT_RULE_EXECUTION_TIMESTAMP]: new Date(), - [ALERT_START]: currentTimeOverride ?? new Date(), - [ALERT_LAST_DETECTED]: currentTimeOverride ?? new Date(), + [ALERT_RULE_EXECUTION_TIMESTAMP]: currentDate, + [ALERT_START]: timestampOverrideOrCurrent, + [ALERT_LAST_DETECTED]: timestampOverrideOrCurrent, [ALERT_INTENDED_TIMESTAMP]: intendedTimestamp ? intendedTimestamp - : currentTimeOverride ?? new Date(), + : timestampOverrideOrCurrent, [VERSION]: kibanaVersion, ...(options?.maintenanceWindowIds?.length ? { [ALERT_MAINTENANCE_WINDOW_IDS]: options.maintenanceWindowIds } From 7264d3a548aef55017b58a1743c81cf1c2aa6c7a Mon Sep 17 00:00:00 2001 From: Saarika Bhasi <55930906+saarikabhasi@users.noreply.github.com> Date: Fri, 13 Sep 2024 07:46:00 -0400 Subject: [PATCH 09/17] [Index Management] [Onboarding] Create new package folder for index_management (#192594) ## Summary We have to extract few components from `index_management` plugin to shared packages for onboarding project. These extracted files would be separated into small subject matter packages within a common folder - `index-management` in `x-pack/packages/`. What is covered in this PR? * Created new folder `index-management` under [ x-pack/packages/](https://github.com/elastic/kibana/tree/main/x-pack/packages) as a home for subject matter packages. * moved existing package - [@kbn/index-management](https://github.com/elastic/kibana/tree/main/x-pack/packages/index-management) under `x-pack/packages/index-management` * update name of [@kbn/index-management](https://github.com/elastic/kibana/tree/main/x-pack/packages/index-management) to `@kbn/index-management-shared-types` * updated related files which use `@kbn/index-management` to use `@kbn/index-management-shared-types` **Note** Extracting components required for onboarding project will be part of another PR --- .github/CODEOWNERS | 2 +- package.json | 2 +- packages/kbn-text-based-editor/src/types.ts | 2 +- packages/kbn-text-based-editor/tsconfig.json | 2 +- src/plugins/esql/public/kibana_services.ts | 2 +- src/plugins/esql/public/plugin.ts | 2 +- src/plugins/esql/tsconfig.json | 2 +- tsconfig.base.json | 4 ++-- .../{ => index_management_shared_types}/README.md | 2 +- .../{ => index_management_shared_types}/index.ts | 0 .../{ => index_management_shared_types}/jest.config.js | 4 ++-- .../{ => index_management_shared_types}/kibana.jsonc | 2 +- .../{ => index_management_shared_types}/package.json | 2 +- .../{ => index_management_shared_types}/src/home_sections.ts | 0 .../src/services/extensions_service.ts | 0 .../src/services/index.ts | 0 .../src/services/public_api_service.ts | 0 .../{ => index_management_shared_types}/src/types.ts | 0 .../{ => index_management_shared_types}/tsconfig.json | 2 +- .../public/applications/shared/kibana/kibana_logic.ts | 2 +- x-pack/plugins/enterprise_search/public/plugin.ts | 2 +- x-pack/plugins/enterprise_search/tsconfig.json | 2 +- .../components/index_lifecycle_summary.tsx | 2 +- x-pack/plugins/index_lifecycle_management/tsconfig.json | 2 +- x-pack/plugins/index_management/common/constants/index.ts | 4 ++-- .../plugins/index_management/common/lib/enrich_policies.ts | 2 +- x-pack/plugins/index_management/common/types/indices.ts | 2 +- .../public/application/hooks/use_index_errors.ts | 2 +- .../sections/enrich_policy_create/create_policy_context.tsx | 2 +- .../sections/enrich_policy_create/create_policy_wizard.tsx | 2 +- .../sections/enrich_policy_create/steps/create.tsx | 2 +- .../details_flyout/policy_details_flyout.tsx | 2 +- .../home/enrich_policies_list/enrich_policies_list.tsx | 2 +- .../enrich_policies_list/policies_table/policies_table.tsx | 2 +- .../details_page/index_mapping_with_context_types.tsx | 2 +- .../index_management/public/application/services/api.ts | 2 +- x-pack/plugins/index_management/public/index.ts | 2 +- x-pack/plugins/index_management/public/plugin.ts | 5 ++++- .../public/services/extensions_service.mock.ts | 2 +- .../index_management/public/services/extensions_service.ts | 2 +- .../public/services/public_api_service.mock.ts | 2 +- .../plugins/index_management/server/lib/enrich_policies.ts | 2 +- .../routes/api/enrich_policies/register_create_route.ts | 2 +- x-pack/plugins/index_management/tsconfig.json | 2 +- x-pack/plugins/search_indices/public/hooks/api/use_index.ts | 2 +- x-pack/plugins/search_indices/tsconfig.json | 2 +- .../components/index_management/index_mappings_docs_link.tsx | 2 +- .../components/index_management/index_overview_content.tsx | 2 +- x-pack/plugins/serverless_search/tsconfig.json | 2 +- yarn.lock | 2 +- 50 files changed, 50 insertions(+), 47 deletions(-) rename x-pack/packages/index-management/{ => index_management_shared_types}/README.md (77%) rename x-pack/packages/index-management/{ => index_management_shared_types}/index.ts (100%) rename x-pack/packages/index-management/{ => index_management_shared_types}/jest.config.js (72%) rename x-pack/packages/index-management/{ => index_management_shared_types}/kibana.jsonc (60%) rename x-pack/packages/index-management/{ => index_management_shared_types}/package.json (62%) rename x-pack/packages/index-management/{ => index_management_shared_types}/src/home_sections.ts (100%) rename x-pack/packages/index-management/{ => index_management_shared_types}/src/services/extensions_service.ts (100%) rename x-pack/packages/index-management/{ => index_management_shared_types}/src/services/index.ts (100%) rename x-pack/packages/index-management/{ => index_management_shared_types}/src/services/public_api_service.ts (100%) rename x-pack/packages/index-management/{ => index_management_shared_types}/src/types.ts (100%) rename x-pack/packages/index-management/{ => index_management_shared_types}/tsconfig.json (85%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 60a15b9372e81..3a4306292b8f9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -508,8 +508,8 @@ src/plugins/image_embeddable @elastic/appex-sharedux packages/kbn-import-locator @elastic/kibana-operations packages/kbn-import-resolver @elastic/kibana-operations x-pack/plugins/index_lifecycle_management @elastic/kibana-management -x-pack/packages/index-management @elastic/kibana-management x-pack/plugins/index_management @elastic/kibana-management +x-pack/packages/index-management/index_management_shared_types @elastic/kibana-management test/plugin_functional/plugins/index_patterns @elastic/kibana-data-discovery x-pack/packages/ml/inference_integration_flyout @elastic/ml-ui x-pack/plugins/inference @elastic/appex-ai-infra diff --git a/package.json b/package.json index 33822a9b43f61..5aec5cd34dde8 100644 --- a/package.json +++ b/package.json @@ -552,8 +552,8 @@ "@kbn/iframe-embedded-plugin": "link:x-pack/test/functional_embedded/plugins/iframe_embedded", "@kbn/image-embeddable-plugin": "link:src/plugins/image_embeddable", "@kbn/index-lifecycle-management-plugin": "link:x-pack/plugins/index_lifecycle_management", - "@kbn/index-management": "link:x-pack/packages/index-management", "@kbn/index-management-plugin": "link:x-pack/plugins/index_management", + "@kbn/index-management-shared-types": "link:x-pack/packages/index-management/index_management_shared_types", "@kbn/index-patterns-test-plugin": "link:test/plugin_functional/plugins/index_patterns", "@kbn/inference-plugin": "link:x-pack/plugins/inference", "@kbn/inference_integration_flyout": "link:x-pack/packages/ml/inference_integration_flyout", diff --git a/packages/kbn-text-based-editor/src/types.ts b/packages/kbn-text-based-editor/src/types.ts index 4dabf29308486..69968d528dbad 100644 --- a/packages/kbn-text-based-editor/src/types.ts +++ b/packages/kbn-text-based-editor/src/types.ts @@ -11,7 +11,7 @@ import type { CoreStart } from '@kbn/core/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { AggregateQuery } from '@kbn/es-query'; import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; -import type { IndexManagementPluginSetup } from '@kbn/index-management'; +import type { IndexManagementPluginSetup } from '@kbn/index-management-shared-types'; import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public'; export interface TextBasedLanguagesEditorProps { diff --git a/packages/kbn-text-based-editor/tsconfig.json b/packages/kbn-text-based-editor/tsconfig.json index b5ebfb8526df0..3b51e4095c7e3 100644 --- a/packages/kbn-text-based-editor/tsconfig.json +++ b/packages/kbn-text-based-editor/tsconfig.json @@ -23,7 +23,7 @@ "@kbn/data-plugin", "@kbn/expressions-plugin", "@kbn/data-views-plugin", - "@kbn/index-management", + "@kbn/index-management-shared-types", "@kbn/code-editor", "@kbn/shared-ux-markdown", "@kbn/fields-metadata-plugin", diff --git a/src/plugins/esql/public/kibana_services.ts b/src/plugins/esql/public/kibana_services.ts index b8679d38f7852..ae6eca13715f5 100644 --- a/src/plugins/esql/public/kibana_services.ts +++ b/src/plugins/esql/public/kibana_services.ts @@ -11,7 +11,7 @@ import { BehaviorSubject } from 'rxjs'; import type { CoreStart } from '@kbn/core/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; -import type { IndexManagementPluginSetup } from '@kbn/index-management'; +import type { IndexManagementPluginSetup } from '@kbn/index-management-shared-types'; import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public'; export let core: CoreStart; diff --git a/src/plugins/esql/public/plugin.ts b/src/plugins/esql/public/plugin.ts index a0220a5e95aaf..ca75c27eccdca 100755 --- a/src/plugins/esql/public/plugin.ts +++ b/src/plugins/esql/public/plugin.ts @@ -11,7 +11,7 @@ import type { Plugin, CoreStart, CoreSetup } from '@kbn/core/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import type { IndexManagementPluginSetup } from '@kbn/index-management'; +import type { IndexManagementPluginSetup } from '@kbn/index-management-shared-types'; import type { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public'; import { diff --git a/src/plugins/esql/tsconfig.json b/src/plugins/esql/tsconfig.json index 500ff09276eaa..6420a9535197d 100644 --- a/src/plugins/esql/tsconfig.json +++ b/src/plugins/esql/tsconfig.json @@ -15,7 +15,7 @@ "@kbn/core", "@kbn/expressions-plugin", "@kbn/data-views-plugin", - "@kbn/index-management", + "@kbn/index-management-shared-types", "@kbn/i18n", "@kbn/config-schema", "@kbn/esql-utils", diff --git a/tsconfig.base.json b/tsconfig.base.json index 3c30cdbda22c9..f931b2e3ea28d 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1010,10 +1010,10 @@ "@kbn/import-resolver/*": ["packages/kbn-import-resolver/*"], "@kbn/index-lifecycle-management-plugin": ["x-pack/plugins/index_lifecycle_management"], "@kbn/index-lifecycle-management-plugin/*": ["x-pack/plugins/index_lifecycle_management/*"], - "@kbn/index-management": ["x-pack/packages/index-management"], - "@kbn/index-management/*": ["x-pack/packages/index-management/*"], "@kbn/index-management-plugin": ["x-pack/plugins/index_management"], "@kbn/index-management-plugin/*": ["x-pack/plugins/index_management/*"], + "@kbn/index-management-shared-types": ["x-pack/packages/index-management/index_management_shared_types"], + "@kbn/index-management-shared-types/*": ["x-pack/packages/index-management/index_management_shared_types/*"], "@kbn/index-patterns-test-plugin": ["test/plugin_functional/plugins/index_patterns"], "@kbn/index-patterns-test-plugin/*": ["test/plugin_functional/plugins/index_patterns/*"], "@kbn/inference_integration_flyout": ["x-pack/packages/ml/inference_integration_flyout"], diff --git a/x-pack/packages/index-management/README.md b/x-pack/packages/index-management/index_management_shared_types/README.md similarity index 77% rename from x-pack/packages/index-management/README.md rename to x-pack/packages/index-management/index_management_shared_types/README.md index 438a6b4393892..172f59136ada4 100644 --- a/x-pack/packages/index-management/README.md +++ b/x-pack/packages/index-management/index_management_shared_types/README.md @@ -1,3 +1,3 @@ -# @kbn/index-management +# @kbn/index-management-shared-types Contains types and functions used and exported by the index management plugin. Primarily used to avoid cyclical dependencies. diff --git a/x-pack/packages/index-management/index.ts b/x-pack/packages/index-management/index_management_shared_types/index.ts similarity index 100% rename from x-pack/packages/index-management/index.ts rename to x-pack/packages/index-management/index_management_shared_types/index.ts diff --git a/x-pack/packages/index-management/jest.config.js b/x-pack/packages/index-management/index_management_shared_types/jest.config.js similarity index 72% rename from x-pack/packages/index-management/jest.config.js rename to x-pack/packages/index-management/index_management_shared_types/jest.config.js index 0d2b7feaaf550..c9238a37a56ab 100644 --- a/x-pack/packages/index-management/jest.config.js +++ b/x-pack/packages/index-management/index_management_shared_types/jest.config.js @@ -7,6 +7,6 @@ module.exports = { preset: '@kbn/test', - rootDir: '../../..', - roots: ['/x-pack/packages/index-management'], + rootDir: '../../../..', + roots: ['/x-pack/packages/index-management/index_management_shared_types'], }; diff --git a/x-pack/packages/index-management/kibana.jsonc b/x-pack/packages/index-management/index_management_shared_types/kibana.jsonc similarity index 60% rename from x-pack/packages/index-management/kibana.jsonc rename to x-pack/packages/index-management/index_management_shared_types/kibana.jsonc index dc4e61342c6bc..ad87bb2bb4797 100644 --- a/x-pack/packages/index-management/kibana.jsonc +++ b/x-pack/packages/index-management/index_management_shared_types/kibana.jsonc @@ -1,5 +1,5 @@ { "type": "shared-common", - "id": "@kbn/index-management", + "id": "@kbn/index-management-shared-types", "owner": "@elastic/kibana-management" } diff --git a/x-pack/packages/index-management/package.json b/x-pack/packages/index-management/index_management_shared_types/package.json similarity index 62% rename from x-pack/packages/index-management/package.json rename to x-pack/packages/index-management/index_management_shared_types/package.json index c1f7664895348..be6b3a888953d 100644 --- a/x-pack/packages/index-management/package.json +++ b/x-pack/packages/index-management/index_management_shared_types/package.json @@ -1,5 +1,5 @@ { - "name": "@kbn/index-management", + "name": "@kbn/index-management-shared-types", "private": true, "version": "1.0.0", "license": "Elastic License 2.0" diff --git a/x-pack/packages/index-management/src/home_sections.ts b/x-pack/packages/index-management/index_management_shared_types/src/home_sections.ts similarity index 100% rename from x-pack/packages/index-management/src/home_sections.ts rename to x-pack/packages/index-management/index_management_shared_types/src/home_sections.ts diff --git a/x-pack/packages/index-management/src/services/extensions_service.ts b/x-pack/packages/index-management/index_management_shared_types/src/services/extensions_service.ts similarity index 100% rename from x-pack/packages/index-management/src/services/extensions_service.ts rename to x-pack/packages/index-management/index_management_shared_types/src/services/extensions_service.ts diff --git a/x-pack/packages/index-management/src/services/index.ts b/x-pack/packages/index-management/index_management_shared_types/src/services/index.ts similarity index 100% rename from x-pack/packages/index-management/src/services/index.ts rename to x-pack/packages/index-management/index_management_shared_types/src/services/index.ts diff --git a/x-pack/packages/index-management/src/services/public_api_service.ts b/x-pack/packages/index-management/index_management_shared_types/src/services/public_api_service.ts similarity index 100% rename from x-pack/packages/index-management/src/services/public_api_service.ts rename to x-pack/packages/index-management/index_management_shared_types/src/services/public_api_service.ts diff --git a/x-pack/packages/index-management/src/types.ts b/x-pack/packages/index-management/index_management_shared_types/src/types.ts similarity index 100% rename from x-pack/packages/index-management/src/types.ts rename to x-pack/packages/index-management/index_management_shared_types/src/types.ts diff --git a/x-pack/packages/index-management/tsconfig.json b/x-pack/packages/index-management/index_management_shared_types/tsconfig.json similarity index 85% rename from x-pack/packages/index-management/tsconfig.json rename to x-pack/packages/index-management/index_management_shared_types/tsconfig.json index c92c31a7ea49b..351991448dba7 100644 --- a/x-pack/packages/index-management/tsconfig.json +++ b/x-pack/packages/index-management/index_management_shared_types/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.base.json", + "extends": "../../../../tsconfig.base.json", "compilerOptions": { "outDir": "target/types", "types": [ diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts index da5209a9dc1ec..2338036ccf1e8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts @@ -24,7 +24,7 @@ import { import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; -import { IndexMappingProps } from '@kbn/index-management'; +import { IndexMappingProps } from '@kbn/index-management-shared-types'; import { LensPublicStart } from '@kbn/lens-plugin/public'; import { MlPluginStart } from '@kbn/ml-plugin/public'; import { ELASTICSEARCH_URL_PLACEHOLDER } from '@kbn/search-api-panels/constants'; diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts index 2ea1c55e6e037..73dbe6885fdcc 100644 --- a/x-pack/plugins/enterprise_search/public/plugin.ts +++ b/x-pack/plugins/enterprise_search/public/plugin.ts @@ -25,7 +25,7 @@ import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import { i18n } from '@kbn/i18n'; -import type { IndexManagementPluginStart } from '@kbn/index-management'; +import type { IndexManagementPluginStart } from '@kbn/index-management-shared-types'; import { LensPublicStart } from '@kbn/lens-plugin/public'; import { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import { MlPluginStart } from '@kbn/ml-plugin/public'; diff --git a/x-pack/plugins/enterprise_search/tsconfig.json b/x-pack/plugins/enterprise_search/tsconfig.json index 58b1526e14baf..841cebf28cf46 100644 --- a/x-pack/plugins/enterprise_search/tsconfig.json +++ b/x-pack/plugins/enterprise_search/tsconfig.json @@ -72,7 +72,7 @@ "@kbn/search-playground", "@kbn/search-inference-endpoints", "@kbn/utility-types", - "@kbn/index-management", + "@kbn/index-management-shared-types", "@kbn/deeplinks-search", "@kbn/react-kibana-context-theme", "@kbn/search-types", diff --git a/x-pack/plugins/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx b/x-pack/plugins/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx index 1d828a758b277..ac96deaef92e4 100644 --- a/x-pack/plugins/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/extend_index_management/components/index_lifecycle_summary.tsx @@ -26,7 +26,7 @@ import { import { euiThemeVars } from '@kbn/ui-theme'; import { ApplicationStart } from '@kbn/core/public'; -import { Index, IndexDetailsTab } from '@kbn/index-management'; +import { Index, IndexDetailsTab } from '@kbn/index-management-shared-types'; import { IlmExplainLifecycleLifecycleExplainManaged } from '@elastic/elasticsearch/lib/api/types'; import { Phase } from '../../../common/types'; import { getPolicyEditPath } from '../../application/services/navigation'; diff --git a/x-pack/plugins/index_lifecycle_management/tsconfig.json b/x-pack/plugins/index_lifecycle_management/tsconfig.json index a70e757e8a8e0..7c3913fcae1bd 100644 --- a/x-pack/plugins/index_lifecycle_management/tsconfig.json +++ b/x-pack/plugins/index_lifecycle_management/tsconfig.json @@ -37,7 +37,7 @@ "@kbn/shared-ux-router", "@kbn/ui-theme", "@kbn/shared-ux-link-redirect-app", - "@kbn/index-management", + "@kbn/index-management-shared-types", "@kbn/react-kibana-context-render", "@kbn/unsaved-changes-prompt", "@kbn/shared-ux-table-persist", diff --git a/x-pack/plugins/index_management/common/constants/index.ts b/x-pack/plugins/index_management/common/constants/index.ts index abb931468498c..2174985095ea5 100644 --- a/x-pack/plugins/index_management/common/constants/index.ts +++ b/x-pack/plugins/index_management/common/constants/index.ts @@ -54,6 +54,6 @@ export { export { MAJOR_VERSION } from './plugin'; -export { Section, IndexDetailsSection } from '@kbn/index-management'; -export type { IndexDetailsTab, IndexDetailsTabId } from '@kbn/index-management'; +export { Section, IndexDetailsSection } from '@kbn/index-management-shared-types'; +export type { IndexDetailsTab, IndexDetailsTabId } from '@kbn/index-management-shared-types'; export * from './allow_auto_create'; diff --git a/x-pack/plugins/index_management/common/lib/enrich_policies.ts b/x-pack/plugins/index_management/common/lib/enrich_policies.ts index fc7e15addec86..7c3d4852b5e64 100644 --- a/x-pack/plugins/index_management/common/lib/enrich_policies.ts +++ b/x-pack/plugins/index_management/common/lib/enrich_policies.ts @@ -6,7 +6,7 @@ */ import type { EnrichSummary, EnrichPolicyType } from '@elastic/elasticsearch/lib/api/types'; -import type { SerializedEnrichPolicy } from '@kbn/index-management'; +import type { SerializedEnrichPolicy } from '@kbn/index-management-shared-types'; export const getPolicyType = (policy: EnrichSummary): EnrichPolicyType => { if (policy.config.match) { diff --git a/x-pack/plugins/index_management/common/types/indices.ts b/x-pack/plugins/index_management/common/types/indices.ts index 555fe1c331ae0..612aaf3bd6c9b 100644 --- a/x-pack/plugins/index_management/common/types/indices.ts +++ b/x-pack/plugins/index_management/common/types/indices.ts @@ -5,7 +5,7 @@ * 2.0. */ -export type { Index } from '@kbn/index-management'; +export type { Index } from '@kbn/index-management-shared-types'; export interface IndexModule { number_of_shards: number | string; diff --git a/x-pack/plugins/index_management/public/application/hooks/use_index_errors.ts b/x-pack/plugins/index_management/public/application/hooks/use_index_errors.ts index 58c3a359c2dce..82f1545e3a98a 100644 --- a/x-pack/plugins/index_management/public/application/hooks/use_index_errors.ts +++ b/x-pack/plugins/index_management/public/application/hooks/use_index_errors.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { Index } from '@kbn/index-management'; +import { Index } from '@kbn/index-management-shared-types'; import { MlPluginStart } from '@kbn/ml-plugin/public'; import { useState, useEffect } from 'react'; import { normalize } from '../components/mappings_editor/lib'; diff --git a/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/create_policy_context.tsx b/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/create_policy_context.tsx index dab8056486405..5ea137b717a0d 100644 --- a/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/create_policy_context.tsx +++ b/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/create_policy_context.tsx @@ -6,7 +6,7 @@ */ import React, { createContext, useContext, useState } from 'react'; -import type { SerializedEnrichPolicy } from '@kbn/index-management'; +import type { SerializedEnrichPolicy } from '@kbn/index-management-shared-types'; export type DraftPolicy = Partial; diff --git a/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/create_policy_wizard.tsx b/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/create_policy_wizard.tsx index ff20f78830476..ab39f06fbb410 100644 --- a/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/create_policy_wizard.tsx +++ b/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/create_policy_wizard.tsx @@ -9,7 +9,7 @@ import React, { useState, useMemo, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiSteps, EuiStepStatus, EuiCallOut, EuiSpacer } from '@elastic/eui'; -import type { SerializedEnrichPolicy } from '@kbn/index-management'; +import type { SerializedEnrichPolicy } from '@kbn/index-management-shared-types'; import { useAppContext } from '../../app_context'; import { ConfigurationStep, FieldSelectionStep, CreateStep } from './steps'; import { useCreatePolicyContext } from './create_policy_context'; diff --git a/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/steps/create.tsx b/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/steps/create.tsx index 7150e2bd29bac..a8f97d7793f50 100644 --- a/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/steps/create.tsx +++ b/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/steps/create.tsx @@ -21,7 +21,7 @@ import { EuiCodeBlock, } from '@elastic/eui'; -import type { SerializedEnrichPolicy } from '@kbn/index-management'; +import type { SerializedEnrichPolicy } from '@kbn/index-management-shared-types'; import { useCreatePolicyContext } from '../create_policy_context'; import { serializeAsESPolicy, getESPolicyCreationApiCall } from '../../../../../common/lib'; diff --git a/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/details_flyout/policy_details_flyout.tsx b/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/details_flyout/policy_details_flyout.tsx index 9fb20e33e22c8..2e8c4d9c5ef6e 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/details_flyout/policy_details_flyout.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/details_flyout/policy_details_flyout.tsx @@ -21,7 +21,7 @@ import { EuiButtonEmpty, } from '@elastic/eui'; import { CodeEditor } from '@kbn/code-editor'; -import type { SerializedEnrichPolicy } from '@kbn/index-management'; +import type { SerializedEnrichPolicy } from '@kbn/index-management-shared-types'; export interface Props { policy: SerializedEnrichPolicy; diff --git a/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/enrich_policies_list.tsx b/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/enrich_policies_list.tsx index e11c0e9db87fc..a036273ebdde9 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/enrich_policies_list.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/enrich_policies_list.tsx @@ -12,7 +12,7 @@ import { RouteComponentProps } from 'react-router-dom'; import { Location } from 'history'; import { parse } from 'query-string'; -import type { SerializedEnrichPolicy } from '@kbn/index-management'; +import type { SerializedEnrichPolicy } from '@kbn/index-management-shared-types'; import { APP_WRAPPER_CLASS, useExecutionContext } from '../../../../shared_imports'; import { useAppContext } from '../../../app_context'; import { useRedirectPath } from '../../../hooks/redirect_path'; diff --git a/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/policies_table/policies_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/policies_table/policies_table.tsx index 4c3ec85e16ea7..e78e08b829997 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/policies_table/policies_table.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/policies_table/policies_table.tsx @@ -16,7 +16,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { reactRouterNavigate } from '@kbn/kibana-react-plugin/public'; -import type { SerializedEnrichPolicy } from '@kbn/index-management'; +import type { SerializedEnrichPolicy } from '@kbn/index-management-shared-types'; import { useEuiTablePersist, DEFAULT_PAGE_SIZE_OPTIONS } from '@kbn/shared-ux-table-persist'; import { useAppContext } from '../../../../app_context'; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/index_mapping_with_context_types.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/index_mapping_with_context_types.tsx index 5dd264d38b80e..228f928f1ec74 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/index_mapping_with_context_types.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/index_mapping_with_context_types.tsx @@ -6,7 +6,7 @@ */ import { CoreStart } from '@kbn/core/public'; -import { IndexMappingProps } from '@kbn/index-management'; +import { IndexMappingProps } from '@kbn/index-management-shared-types'; import { AppDependencies } from '../../../../app_context'; import { ExtensionsService } from '../../../../../services/extensions_service'; diff --git a/x-pack/plugins/index_management/public/application/services/api.ts b/x-pack/plugins/index_management/public/application/services/api.ts index f182ffe53ce07..08baa49713573 100644 --- a/x-pack/plugins/index_management/public/application/services/api.ts +++ b/x-pack/plugins/index_management/public/application/services/api.ts @@ -6,7 +6,7 @@ */ import { METRIC_TYPE } from '@kbn/analytics'; -import type { SerializedEnrichPolicy } from '@kbn/index-management'; +import type { SerializedEnrichPolicy } from '@kbn/index-management-shared-types'; import { IndicesStatsResponse } from '@elastic/elasticsearch/lib/api/types'; import { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; import { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; diff --git a/x-pack/plugins/index_management/public/index.ts b/x-pack/plugins/index_management/public/index.ts index 09adb299c711f..e49b83b892add 100644 --- a/x-pack/plugins/index_management/public/index.ts +++ b/x-pack/plugins/index_management/public/index.ts @@ -19,6 +19,6 @@ export type { IndexMappingProps, IndexManagementPluginSetup, IndexManagementPluginStart, -} from '@kbn/index-management'; +} from '@kbn/index-management-shared-types'; export { getIndexListUri, getTemplateDetailsLink } from './application/services/routing'; diff --git a/x-pack/plugins/index_management/public/plugin.ts b/x-pack/plugins/index_management/public/plugin.ts index dff4880fbc66a..49693bb0d9aa9 100644 --- a/x-pack/plugins/index_management/public/plugin.ts +++ b/x-pack/plugins/index_management/public/plugin.ts @@ -15,7 +15,10 @@ import { PluginInitializerContext, ScopedHistory, } from '@kbn/core/public'; -import { IndexManagementPluginSetup, IndexManagementPluginStart } from '@kbn/index-management'; +import { + IndexManagementPluginSetup, + IndexManagementPluginStart, +} from '@kbn/index-management-shared-types'; import { setExtensionsService } from './application/store/selectors/extension_service'; import { ExtensionsService } from './services/extensions_service'; diff --git a/x-pack/plugins/index_management/public/services/extensions_service.mock.ts b/x-pack/plugins/index_management/public/services/extensions_service.mock.ts index 072acd92f5a9d..c2f80cb4e8e95 100644 --- a/x-pack/plugins/index_management/public/services/extensions_service.mock.ts +++ b/x-pack/plugins/index_management/public/services/extensions_service.mock.ts @@ -6,7 +6,7 @@ */ import type { PublicMethodsOf } from '@kbn/utility-types'; -import { ExtensionsSetup } from '@kbn/index-management/src/services/extensions_service'; +import { ExtensionsSetup } from '@kbn/index-management-shared-types/src/services/extensions_service'; import { ExtensionsService } from './extensions_service'; export type ExtensionsSetupMock = jest.Mocked; diff --git a/x-pack/plugins/index_management/public/services/extensions_service.ts b/x-pack/plugins/index_management/public/services/extensions_service.ts index 3823ad7ed28f2..116007ba1276f 100644 --- a/x-pack/plugins/index_management/public/services/extensions_service.ts +++ b/x-pack/plugins/index_management/public/services/extensions_service.ts @@ -13,7 +13,7 @@ import { EmptyListContent, IndexContent, ExtensionsSetup, -} from '@kbn/index-management'; +} from '@kbn/index-management-shared-types'; import { IndexDetailsTab } from '../../common/constants'; export class ExtensionsService { diff --git a/x-pack/plugins/index_management/public/services/public_api_service.mock.ts b/x-pack/plugins/index_management/public/services/public_api_service.mock.ts index 567c87b914654..b78ce424cbc1a 100644 --- a/x-pack/plugins/index_management/public/services/public_api_service.mock.ts +++ b/x-pack/plugins/index_management/public/services/public_api_service.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { PublicApiServiceSetup } from '@kbn/index-management/src/services/public_api_service'; +import { PublicApiServiceSetup } from '@kbn/index-management-shared-types/src/services/public_api_service'; export type PublicApiServiceSetupMock = jest.Mocked; diff --git a/x-pack/plugins/index_management/server/lib/enrich_policies.ts b/x-pack/plugins/index_management/server/lib/enrich_policies.ts index 61fa1d1467006..119a865d49661 100644 --- a/x-pack/plugins/index_management/server/lib/enrich_policies.ts +++ b/x-pack/plugins/index_management/server/lib/enrich_policies.ts @@ -7,7 +7,7 @@ import { IScopedClusterClient } from '@kbn/core/server'; import type { EnrichSummary } from '@elastic/elasticsearch/lib/api/types'; -import type { SerializedEnrichPolicy } from '@kbn/index-management'; +import type { SerializedEnrichPolicy } from '@kbn/index-management-shared-types'; import { getPolicyType } from '../../common/lib'; export const serializeEnrichmentPolicies = ( diff --git a/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_create_route.ts b/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_create_route.ts index 24110cc685676..bb6ef8b1fff50 100644 --- a/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_create_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_create_route.ts @@ -8,7 +8,7 @@ import { IScopedClusterClient } from '@kbn/core/server'; import { schema, TypeOf } from '@kbn/config-schema'; -import type { SerializedEnrichPolicy } from '@kbn/index-management'; +import type { SerializedEnrichPolicy } from '@kbn/index-management-shared-types'; import { RouteDependencies } from '../../../types'; import { addInternalBasePath } from '..'; import { enrichPoliciesActions } from '../../../lib/enrich_policies'; diff --git a/x-pack/plugins/index_management/tsconfig.json b/x-pack/plugins/index_management/tsconfig.json index e8244c57beef5..eac67aa620973 100644 --- a/x-pack/plugins/index_management/tsconfig.json +++ b/x-pack/plugins/index_management/tsconfig.json @@ -45,7 +45,7 @@ "@kbn/monaco", "@kbn/console-plugin", "@kbn/shared-ux-utility", - "@kbn/index-management", + "@kbn/index-management-shared-types", "@kbn/utility-types", "@kbn/inference_integration_flyout", "@kbn/ml-plugin", diff --git a/x-pack/plugins/search_indices/public/hooks/api/use_index.ts b/x-pack/plugins/search_indices/public/hooks/api/use_index.ts index 65a8710589393..b872d2044ff2c 100644 --- a/x-pack/plugins/search_indices/public/hooks/api/use_index.ts +++ b/x-pack/plugins/search_indices/public/hooks/api/use_index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { Index } from '@kbn/index-management'; +import type { Index } from '@kbn/index-management-shared-types'; import { useQuery } from '@tanstack/react-query'; import { useKibana } from '../use_kibana'; diff --git a/x-pack/plugins/search_indices/tsconfig.json b/x-pack/plugins/search_indices/tsconfig.json index c8f2397b39a55..724daa51ba9a4 100644 --- a/x-pack/plugins/search_indices/tsconfig.json +++ b/x-pack/plugins/search_indices/tsconfig.json @@ -27,7 +27,7 @@ "@kbn/share-plugin", "@kbn/kibana-utils-plugin", "@kbn/shared-ux-router", - "@kbn/index-management", + "@kbn/index-management-shared-types", "@kbn/try-in-console", "@kbn/cloud-plugin", ], diff --git a/x-pack/plugins/serverless_search/public/application/components/index_management/index_mappings_docs_link.tsx b/x-pack/plugins/serverless_search/public/application/components/index_management/index_mappings_docs_link.tsx index d75a96ba45e6f..860fe28a82298 100644 --- a/x-pack/plugins/serverless_search/public/application/components/index_management/index_mappings_docs_link.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/index_management/index_mappings_docs_link.tsx @@ -18,7 +18,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { CoreStart } from '@kbn/core/public'; -import { IndexContent } from '@kbn/index-management'; +import { IndexContent } from '@kbn/index-management-shared-types'; const IndexMappingsDocsLink: FunctionComponent<{ docLinks: CoreStart['docLinks'] }> = ({ docLinks, diff --git a/x-pack/plugins/serverless_search/public/application/components/index_management/index_overview_content.tsx b/x-pack/plugins/serverless_search/public/application/components/index_management/index_overview_content.tsx index c1d31dd50a20c..34b07ce39cd94 100644 --- a/x-pack/plugins/serverless_search/public/application/components/index_management/index_overview_content.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/index_management/index_overview_content.tsx @@ -12,7 +12,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { EuiLoadingSpinner } from '@elastic/eui'; -import { IndexContent } from '@kbn/index-management'; +import { IndexContent } from '@kbn/index-management-shared-types'; import { ServerlessSearchPluginStartDependencies } from '../../../types'; diff --git a/x-pack/plugins/serverless_search/tsconfig.json b/x-pack/plugins/serverless_search/tsconfig.json index c19ec3af2f4fc..642ec77cd3213 100644 --- a/x-pack/plugins/serverless_search/tsconfig.json +++ b/x-pack/plugins/serverless_search/tsconfig.json @@ -45,7 +45,7 @@ "@kbn/core-logging-server-mocks", "@kbn/discover-plugin", "@kbn/search-connectors-plugin", - "@kbn/index-management", + "@kbn/index-management-shared-types", "@kbn/react-kibana-context-render", "@kbn/search-playground", "@kbn/security-api-key-management", diff --git a/yarn.lock b/yarn.lock index 74c9e419d0b2a..999921a5f9210 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5302,7 +5302,7 @@ version "0.0.0" uid "" -"@kbn/index-management@link:x-pack/packages/index-management": +"@kbn/index-management-shared-types@link:x-pack/packages/index-management/index_management_shared_types": version "0.0.0" uid "" From c65c2ae4900a9f75db872cee056af35ff5f79cdc Mon Sep 17 00:00:00 2001 From: Nikita Indik Date: Fri, 13 Sep 2024 14:00:57 +0200 Subject: [PATCH 10/17] [Security Solution] Fix some Prebuilt Rules Cypress tests not running in CI (#191978) **Resolves: https://github.com/elastic/kibana/issues/192256** ## Summary This PR re-enables two Cypress test files that didn't run on CI: `update_workflow.cy.ts` and `prebuilt_rules_preview.cy.ts`. It also fixes failing tests in `prebuilt_rules_preview.cy.ts`. ### Changes - Renamed `update_workflow.ts` -> `update_workflow.cy.ts`. It didn't run on CI because it wasn't picked up by a glob [here](https://github.com/elastic/kibana/blob/main/x-pack/test/security_solution_cypress/package.json#L14). - `prebuilt_rules_preview.cy.ts`: - Moved `{ tags: ['@ess', '@serverless'] }` to the top-level `describe` block instead of having it in a variable that is used in every `describe`. Apparently the tool we use to parse tags doesn't recognize tags in variables anymore, so this test didn't run in either ESS or Serverless pipelines. - Removed `describe('All environments' ... ` wrappers since they don't add any value anymore. Didn't remove any actual tests. - Reverted a change from this [PR](https://github.com/elastic/kibana/pull/181427) that added a backdrop to the modal which doesn't allow user to switch rules without closing the modal. We have a [test](https://github.com/elastic/kibana/blob/main/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts#L1182) that checks that such switching is possible and this test started to fail once I reactivated the test file. - Fixed selectors that grab filters in the Overview tab. The old ones stopped working. Probably because of a change to the filters component that is built by another team. #### Correct behaviour: Switching between rules with flyout open https://github.com/user-attachments/assets/da4a0902-657c-45fe-adc1-eb44ad0de798 --- .../installation_and_upgrade.md | 20 +- .../prebuilt_rules_preview.cy.ts | 1295 ++++++++--------- ...date_workflow.ts => update_workflow.cy.ts} | 0 .../cypress/screens/alerts_detection_rules.ts | 6 +- .../cypress/tasks/prebuilt_rules_preview.ts | 2 +- 5 files changed, 643 insertions(+), 680 deletions(-) rename x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/{update_workflow.ts => update_workflow.cy.ts} (100%) diff --git a/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md index beda8a9517830..e5479ec502865 100644 --- a/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md +++ b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md @@ -631,13 +631,9 @@ Given no prebuilt rules are installed in Kibana And there are X prebuilt rules of all types available to install When user opens the Add Rules page Then all X rules available for installation should be displayed in the table -When user opens the rule preview for the 1st rule -Then the preview should open -And all properties of the 1st rule should be displayed in the correct tab and section of the preview (see examples of rule properties above) -When user selects the 2nd rule in the table -Then the preview should be updated -And all properties of the 2nd rule should be displayed in the correct tab and section of the preview (see examples of rule properties above) -And user should be able to repeat this for all X rules +When user opens a rule preview for any rule +Then the preview should appear +And all properties of a rule should be displayed in the correct tab and section of the preview (see examples of rule properties above) ``` #### **Scenario: Tabs and sections without content should be hidden in preview before installing** @@ -783,15 +779,11 @@ And for all of the installed rules there are new versions available And user is on the Rule Management page When user opens the Rule Updates table Then all X rules available for upgrade should be displayed in the table -When user opens the rule preview for the 1st rule -Then the preview should open +When user opens a rule preview for any rule +Then the preview should appear And the "Updates" tab should be active When user selects the "Overview" tab -Then all properties of the new version of the 1st rule should be displayed in the correct tab and section of the preview (see examples of rule properties above) -When user selects the 2nd rule in the table -Then the preview should be updated -And all properties of the new version of the 2nd rule should be displayed in the correct tab and section of the preview (see examples of rule properties above) -And user should be able to repeat this for all X rules +Then all properties of the new version of a rule should be displayed in the correct tab and section of the preview (see examples of rule properties above) ``` #### **Scenario: Tabs and sections without content should be hidden in preview before upgrading** diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts index fca78851ddf03..1ccb139133f36 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts @@ -69,253 +69,87 @@ import { } from '../../../../tasks/api_calls/common'; import { enableRules, waitForRulesToFinishExecution } from '../../../../tasks/api_calls/rules'; -const TEST_ENV_TAGS = ['@ess', '@serverless']; - const PREVIEW_TABS = { OVERVIEW: 'Overview', JSON_VIEW: 'JSON view', UPDATES: 'Updates', // Currently open by default on upgrade }; -describe('Detection rules, Prebuilt Rules Installation and Update workflow', () => { - const commonProperties: Partial = { - author: ['Elastic', 'Another author'], - building_block_type: 'default', - severity: 'medium', - severity_mapping: [ - { - field: 'Ransomware.severity', - value: '50', - operator: 'equals', - severity: 'high', - }, - ], - risk_score: 20, - risk_score_mapping: [ - { - field: 'Ransomware.child_processes.score', - operator: 'equals', - risk_score: 30, - value: '', - }, - ], - references: ['https://www.example.com/1', 'https://www.example.com/2'], - false_positives: ['False positive example 1', 'False positive example 2'], - investigation_fields: { - field_names: ['Ransomware.files.path', 'Target.dll.name'], - }, - license: 'MIT', - rule_name_override: 'Endpoint.policy.applied.name', - threat: [ - { - framework: 'MITRE ATT&CK', - tactic: { - id: 'TA0009', - reference: 'https://attack.mitre.org/tactics/TA0009', - name: 'Collection', - }, - technique: [ - { - id: 'T1557', - reference: 'https://attack.mitre.org/techniques/T1557', - name: 'Adversary-in-the-Middle', - subtechnique: [ - { - id: 'T1557.002', - reference: 'https://attack.mitre.org/techniques/T1557/002', - name: 'ARP Cache Poisoning', - }, - ], - }, - ], - }, - ], - timestamp_override: 'Target.process.start', - tags: ['tag-a', 'tag-b'], - related_integrations: [ - { package: 'endpoint', version: '^8.2.0' }, - { package: 'windows', version: '^1.5.0' }, - ], - required_fields: [ - { name: 'event.type', type: 'keyword' }, - { name: 'file.extension', type: 'keyword' }, - ], - timeline_id: '3e827bab-838a-469f-bd1e-5e19a2bff2fd', - timeline_title: 'Alerts Involving a Single User Timeline', - interval: '5m', - from: 'now-360s', - note: 'Investigation guide content', - setup: 'Setup guide content', - }; - - const filters = [ - { - meta: { - disabled: false, - negate: false, - alias: null, - index: 'security-solution-default', - key: 'Endpoint.policy.applied.artifacts.global.identifiers.name', - field: 'Endpoint.policy.applied.artifacts.global.identifiers.name', - value: 'exists', - type: 'exists', - }, - query: { - exists: { - field: 'Endpoint.policy.applied.artifacts.global.identifiers.name', +describe( + 'Detection rules, Prebuilt Rules Installation and Update workflow', + { tags: ['@ess', '@serverless'] }, + () => { + const commonProperties: Partial = { + author: ['Elastic', 'Another author'], + building_block_type: 'default', + severity: 'medium', + severity_mapping: [ + { + field: 'Ransomware.severity', + value: '50', + operator: 'equals', + severity: 'high', }, - }, - $state: { store: 'appState' }, - }, - ]; - - const queryProperties: Partial = { - query: '_id : *', - language: 'kuery', - filters, - }; - - const CUSTOM_QUERY_INDEX_PATTERN_RULE = createRuleAssetSavedObject({ - name: 'Custom query index pattern rule', - rule_id: 'custom_query_index_pattern_rule', - ...(commonProperties as Record), - ...queryProperties, - type: 'query', - index: ['winlogbeat-*', 'logs-endpoint.events.*'], - alert_suppression: { - group_by: [ - 'Endpoint.policy.applied.artifacts.global.identifiers.name', - 'Endpoint.policy.applied.id', ], - duration: { unit: 'm', value: 5 }, - missing_fields_strategy: 'suppress', - }, - }); - - const SAVED_QUERY_DATA_VIEW_RULE = createRuleAssetSavedObject({ - name: 'Custom query data view rule', - rule_id: 'custom_query_data_view_rule', - ...(commonProperties as Record), - type: 'saved_query', - data_view_id: 'my-test-data-view-id', - saved_id: '', - language: 'kuery', - index: ['winlogbeat-*', 'logs-endpoint.events.*'], - alert_suppression: { - group_by: [ - 'Endpoint.policy.applied.artifacts.global.identifiers.name', - 'Endpoint.policy.applied.id', + risk_score: 20, + risk_score_mapping: [ + { + field: 'Ransomware.child_processes.score', + operator: 'equals', + risk_score: 30, + value: '', + }, ], - duration: { unit: 'm', value: 5 }, - missing_fields_strategy: 'suppress', - }, - query: '', - }); - - const MACHINE_LEARNING_RULE = omit( - createRuleAssetSavedObject({ - name: 'Machine learning rule', - rule_id: 'machine_learning_rule', - ...commonProperties, - type: 'machine_learning', - anomaly_threshold: 65, - machine_learning_job_id: ['auth_high_count_logon_events', 'auth_high_count_logon_fails'], - alert_suppression: { - group_by: ['host.name'], - duration: { unit: 'm', value: 5 }, - missing_fields_strategy: 'suppress', + references: ['https://www.example.com/1', 'https://www.example.com/2'], + false_positives: ['False positive example 1', 'False positive example 2'], + investigation_fields: { + field_names: ['Ransomware.files.path', 'Target.dll.name'], }, - }), - ['security-rule.query', 'security-rule.language'] - ) as Omit< - ReturnType, - 'security-rule.query' | 'security-rule.language' - >; - - const THRESHOLD_RULE_INDEX_PATTERN = createRuleAssetSavedObject({ - name: 'Threshold index pattern rule', - rule_id: 'threshold_index_pattern_rule', - ...commonProperties, - ...queryProperties, - type: 'threshold', - language: 'lucene', - index: ['winlogbeat-*', 'logs-endpoint.events.*'], - threshold: { - field: [ - 'Endpoint.policy.applied.artifacts.user.identifiers.name', - 'Endpoint.policy.applied.id', - ], - value: 200, - cardinality: [{ field: 'Ransomware.score', value: 3 }], - }, - alert_suppression: undefined, - }); - - const EQL_INDEX_PATTERN_RULE = createRuleAssetSavedObject({ - name: 'Event correlation index pattern rule', - rule_id: 'eql_index_pattern_rule', - ...commonProperties, - type: 'eql', - language: 'eql', - query: 'process where process.name == "regsvr32.exe"', - index: ['winlogbeat-*', 'logs-endpoint.events.*'], - filters, - alert_suppression: undefined, - }); - - const THREAT_MATCH_INDEX_PATTERN_RULE = createRuleAssetSavedObject({ - name: 'Threat match index pattern rule', - rule_id: 'threat_match_index_pattern_rule', - ...commonProperties, - ...queryProperties, - type: 'threat_match', - language: 'lucene', - index: ['winlogbeat-*', 'logs-endpoint.events.*'], - filters, - threat_query: '@timestamp >= "now-30d/d"', - threat_mapping: [ - { - entries: [ - { - field: 'file.hash.md5', - type: 'mapping', - value: 'threat.indicator.file.hash.md5', + license: 'MIT', + rule_name_override: 'Endpoint.policy.applied.name', + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0009', + reference: 'https://attack.mitre.org/tactics/TA0009', + name: 'Collection', }, - ], - }, - ], - threat_index: ['filebeat-*', 'logs-ti_*'], - threat_filters: [ - { - $state: { store: 'appState' }, - meta: { - disabled: false, - key: 'event.category', - negate: false, - params: { query: 'threat' }, - type: 'phrase', - alias: null, + technique: [ + { + id: 'T1557', + reference: 'https://attack.mitre.org/techniques/T1557', + name: 'Adversary-in-the-Middle', + subtechnique: [ + { + id: 'T1557.002', + reference: 'https://attack.mitre.org/techniques/T1557/002', + name: 'ARP Cache Poisoning', + }, + ], + }, + ], }, - query: { match_phrase: { 'event.category': 'threat' } }, - }, - ], - threat_language: 'kuery', - threat_indicator_path: 'threat.indicator', - alert_suppression: undefined, - }); - - const NEW_TERMS_INDEX_PATTERN_RULE = createRuleAssetSavedObject({ - name: 'New terms index pattern rule', - rule_id: 'new_terms_index_pattern_rule', - ...commonProperties, - ...queryProperties, - type: 'new_terms', - query: '_id: *', - new_terms_fields: ['Endpoint.policy.applied.id', 'Memory_protection.unique_key_v1'], - history_window_start: 'now-9d', - index: ['apm-*-transaction*', 'auditbeat-*'], - language: 'lucene', - filters: [ + ], + timestamp_override: 'Target.process.start', + tags: ['tag-a', 'tag-b'], + related_integrations: [ + { package: 'endpoint', version: '^8.2.0' }, + { package: 'windows', version: '^1.5.0' }, + ], + required_fields: [ + { name: 'event.type', type: 'keyword' }, + { name: 'file.extension', type: 'keyword' }, + ], + timeline_id: '3e827bab-838a-469f-bd1e-5e19a2bff2fd', + timeline_title: 'Alerts Involving a Single User Timeline', + interval: '5m', + from: 'now-360s', + note: 'Investigation guide content', + setup: 'Setup guide content', + }; + + const filters = [ { meta: { disabled: false, @@ -334,138 +168,304 @@ describe('Detection rules, Prebuilt Rules Installation and Update workflow', () }, $state: { store: 'appState' }, }, - ], - alert_suppression: undefined, - }); - - const ESQL_RULE = createRuleAssetSavedObject({ - name: 'ESQL rule', - rule_id: 'esql_rule', - ...commonProperties, - type: 'esql', - language: 'esql', - query: 'FROM .alerts-security.alerts-default | STATS count = COUNT(@timestamp) BY @timestamp', - alert_suppression: { - group_by: [ - 'Endpoint.policy.applied.artifacts.global.identifiers.name', - 'Endpoint.policy.applied.id', + ]; + + const queryProperties: Partial = { + query: '_id : *', + language: 'kuery', + filters, + }; + + const CUSTOM_QUERY_INDEX_PATTERN_RULE = createRuleAssetSavedObject({ + name: 'Custom query index pattern rule', + rule_id: 'custom_query_index_pattern_rule', + ...(commonProperties as Record), + ...queryProperties, + type: 'query', + index: ['winlogbeat-*', 'logs-endpoint.events.*'], + alert_suppression: { + group_by: [ + 'Endpoint.policy.applied.artifacts.global.identifiers.name', + 'Endpoint.policy.applied.id', + ], + duration: { unit: 'm', value: 5 }, + missing_fields_strategy: 'suppress', + }, + }); + + const SAVED_QUERY_DATA_VIEW_RULE = createRuleAssetSavedObject({ + name: 'Custom query data view rule', + rule_id: 'custom_query_data_view_rule', + ...(commonProperties as Record), + type: 'saved_query', + data_view_id: 'my-test-data-view-id', + saved_id: '', + language: 'kuery', + index: ['winlogbeat-*', 'logs-endpoint.events.*'], + alert_suppression: { + group_by: [ + 'Endpoint.policy.applied.artifacts.global.identifiers.name', + 'Endpoint.policy.applied.id', + ], + duration: { unit: 'm', value: 5 }, + missing_fields_strategy: 'suppress', + }, + query: '', + }); + + const MACHINE_LEARNING_RULE = omit( + createRuleAssetSavedObject({ + name: 'Machine learning rule', + rule_id: 'machine_learning_rule', + ...commonProperties, + type: 'machine_learning', + anomaly_threshold: 65, + machine_learning_job_id: ['auth_high_count_logon_events', 'auth_high_count_logon_fails'], + alert_suppression: { + group_by: ['host.name'], + duration: { unit: 'm', value: 5 }, + missing_fields_strategy: 'suppress', + }, + }), + ['security-rule.query', 'security-rule.language'] + ) as Omit< + ReturnType, + 'security-rule.query' | 'security-rule.language' + >; + + const THRESHOLD_RULE_INDEX_PATTERN = createRuleAssetSavedObject({ + name: 'Threshold index pattern rule', + rule_id: 'threshold_index_pattern_rule', + ...commonProperties, + ...queryProperties, + type: 'threshold', + language: 'lucene', + index: ['winlogbeat-*', 'logs-endpoint.events.*'], + threshold: { + field: [ + 'Endpoint.policy.applied.artifacts.user.identifiers.name', + 'Endpoint.policy.applied.id', + ], + value: 200, + cardinality: [{ field: 'Ransomware.score', value: 3 }], + }, + alert_suppression: undefined, + }); + + const EQL_INDEX_PATTERN_RULE = createRuleAssetSavedObject({ + name: 'Event correlation index pattern rule', + rule_id: 'eql_index_pattern_rule', + ...commonProperties, + type: 'eql', + language: 'eql', + query: 'process where process.name == "regsvr32.exe"', + index: ['winlogbeat-*', 'logs-endpoint.events.*'], + filters, + alert_suppression: undefined, + }); + + const THREAT_MATCH_INDEX_PATTERN_RULE = createRuleAssetSavedObject({ + name: 'Threat match index pattern rule', + rule_id: 'threat_match_index_pattern_rule', + ...commonProperties, + ...queryProperties, + type: 'threat_match', + language: 'lucene', + index: ['winlogbeat-*', 'logs-endpoint.events.*'], + filters, + threat_query: '@timestamp >= "now-30d/d"', + threat_mapping: [ + { + entries: [ + { + field: 'file.hash.md5', + type: 'mapping', + value: 'threat.indicator.file.hash.md5', + }, + ], + }, + ], + threat_index: ['filebeat-*', 'logs-ti_*'], + threat_filters: [ + { + $state: { store: 'appState' }, + meta: { + disabled: false, + key: 'event.category', + negate: false, + params: { query: 'threat' }, + type: 'phrase', + alias: null, + }, + query: { match_phrase: { 'event.category': 'threat' } }, + }, + ], + threat_language: 'kuery', + threat_indicator_path: 'threat.indicator', + alert_suppression: undefined, + }); + + const NEW_TERMS_INDEX_PATTERN_RULE = createRuleAssetSavedObject({ + name: 'New terms index pattern rule', + rule_id: 'new_terms_index_pattern_rule', + ...commonProperties, + ...queryProperties, + type: 'new_terms', + query: '_id: *', + new_terms_fields: ['Endpoint.policy.applied.id', 'Memory_protection.unique_key_v1'], + history_window_start: 'now-9d', + index: ['apm-*-transaction*', 'auditbeat-*'], + language: 'lucene', + filters: [ + { + meta: { + disabled: false, + negate: false, + alias: null, + index: 'security-solution-default', + key: 'Endpoint.policy.applied.artifacts.global.identifiers.name', + field: 'Endpoint.policy.applied.artifacts.global.identifiers.name', + value: 'exists', + type: 'exists', + }, + query: { + exists: { + field: 'Endpoint.policy.applied.artifacts.global.identifiers.name', + }, + }, + $state: { store: 'appState' }, + }, ], - duration: { unit: 'm', value: 5 }, - missing_fields_strategy: 'suppress', - }, - }); - - const RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES = createRuleAssetSavedObject({ - name: 'Rule with hidden tabs and sections', - rule_id: 'rule_without_investigation_and_setup_guides', - }); - - const testDataView = { - indexPattern: 'test-*', - name: 'My test data view', - id: 'my-test-data-view-id', - }; - - const testSavedQuery = { - query: 'agent.id: *', - name: 'My test saved query', - filterKey: 'agent.hostname', - }; - - beforeEach(() => { - login(); - resetRulesTableState(); - deleteAlertsAndRules(); - - visitRulesManagementTable(); - }); - - describe('Installation of prebuilt rules', () => { - const RULE_1 = createRuleAssetSavedObject({ - name: 'Test rule 1', - rule_id: 'rule_1', + alert_suppression: undefined, }); - const RULE_2 = createRuleAssetSavedObject({ - name: 'Test rule 2', - rule_id: 'rule_2', + const ESQL_RULE = createRuleAssetSavedObject({ + name: 'ESQL rule', + rule_id: 'esql_rule', + ...commonProperties, + type: 'esql', + language: 'esql', + query: 'FROM .alerts-security.alerts-default | STATS count = COUNT(@timestamp) BY @timestamp', + alert_suppression: { + group_by: [ + 'Endpoint.policy.applied.artifacts.global.identifiers.name', + 'Endpoint.policy.applied.id', + ], + duration: { unit: 'm', value: 5 }, + missing_fields_strategy: 'suppress', + }, + }); + + const RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES = createRuleAssetSavedObject({ + name: 'Rule with hidden tabs and sections', + rule_id: 'rule_without_investigation_and_setup_guides', }); + const testDataView = { + indexPattern: 'test-*', + name: 'My test data view', + id: 'my-test-data-view-id', + }; + + const testSavedQuery = { + query: 'agent.id: *', + name: 'My test saved query', + filterKey: 'agent.hostname', + }; + beforeEach(() => { - installPrebuiltRuleAssets([RULE_1, RULE_2]); - cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform').as( - 'installPrebuiltRules' - ); + login(); + resetRulesTableState(); + deleteAlertsAndRules(); + + visitRulesManagementTable(); }); - describe('Basic functionality', { tags: TEST_ENV_TAGS }, () => { - it('User can preview rules available for installation', () => { - clickAddElasticRulesButton(); + describe('Installation of prebuilt rules', () => { + const RULE_1 = createRuleAssetSavedObject({ + name: 'Test rule 1', + rule_id: 'rule_1', + }); - openRuleInstallPreview(RULE_1['security-rule'].name); - closeRulePreview(); + const RULE_2 = createRuleAssetSavedObject({ + name: 'Test rule 2', + rule_id: 'rule_2', }); - it('User can install a rule using the rule preview', () => { - clickAddElasticRulesButton(); + beforeEach(() => { + installPrebuiltRuleAssets([RULE_1, RULE_2]); + cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform').as( + 'installPrebuiltRules' + ); + }); - openRuleInstallPreview(RULE_1['security-rule'].name); - cy.get(INSTALL_PREBUILT_RULE_BUTTON).click(); - cy.wait('@installPrebuiltRules'); - assertRuleInstallationSuccessToastShown([RULE_1]); + describe('Basic functionality', () => { + it('User can preview rules available for installation', () => { + clickAddElasticRulesButton(); - // Go back to rules table and assert that the rules are installed - cy.get(RULE_MANAGEMENT_PAGE_BREADCRUMB).click(); - assertRulesPresentInInstalledRulesTable([RULE_1]); + openRuleInstallPreview(RULE_1['security-rule'].name); + closeRulePreview(); + }); - clickAddElasticRulesButton(); - assertRulesNotPresentInAddPrebuiltRulesTable([RULE_1]); - }); + it('User can install a rule using the rule preview', () => { + clickAddElasticRulesButton(); - it('Tabs and sections without content should be hidden in preview before installing', () => { - installPrebuiltRuleAssets([RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES]); + openRuleInstallPreview(RULE_1['security-rule'].name); + cy.get(INSTALL_PREBUILT_RULE_BUTTON).click(); + cy.wait('@installPrebuiltRules'); + assertRuleInstallationSuccessToastShown([RULE_1]); - clickAddElasticRulesButton(); + // Go back to rules table and assert that the rules are installed + cy.get(RULE_MANAGEMENT_PAGE_BREADCRUMB).click(); + assertRulesPresentInInstalledRulesTable([RULE_1]); - openRuleInstallPreview(RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES['security-rule'].name); + clickAddElasticRulesButton(); + assertRulesNotPresentInAddPrebuiltRulesTable([RULE_1]); + }); - cy.get(INSTALL_PREBUILT_RULE_PREVIEW).contains('Investigation guide').should('not.exist'); - cy.get(INSTALL_PREBUILT_RULE_PREVIEW).contains('Setup guide').should('not.exist'); - }); - }); + it('Tabs and sections without content should be hidden in preview before installing', () => { + installPrebuiltRuleAssets([RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES]); - describe('User can see correct rule information in preview before installing', () => { - beforeEach(() => { - deleteDataView(testDataView.id); - postDataView(testDataView.indexPattern, testDataView.name, testDataView.id); - - deleteSavedQueries(); - createSavedQuery(testSavedQuery.name, testSavedQuery.query, testSavedQuery.filterKey) - .its('body.id') - .then((id: string) => { - (SAVED_QUERY_DATA_VIEW_RULE['security-rule'] as { saved_id: string }).saved_id = id; - - installPrebuiltRuleAssets([ - CUSTOM_QUERY_INDEX_PATTERN_RULE, - SAVED_QUERY_DATA_VIEW_RULE, - MACHINE_LEARNING_RULE, - THRESHOLD_RULE_INDEX_PATTERN, - EQL_INDEX_PATTERN_RULE, - THREAT_MATCH_INDEX_PATTERN_RULE, - NEW_TERMS_INDEX_PATTERN_RULE, - ESQL_RULE, - RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES, - ]); - }); + clickAddElasticRulesButton(); - fetchMachineLearningModules() - .its('body') - .then((mlModules) => { - cy.wrap(mlModules).as('mlModules'); - }); + openRuleInstallPreview(RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES['security-rule'].name); + + cy.get(INSTALL_PREBUILT_RULE_PREVIEW).contains('Investigation guide').should('not.exist'); + cy.get(INSTALL_PREBUILT_RULE_PREVIEW).contains('Setup guide').should('not.exist'); + }); }); - describe('All environments', { tags: TEST_ENV_TAGS }, () => { + describe('User can see correct rule information in preview before installing', () => { + beforeEach(() => { + deleteDataView(testDataView.id); + postDataView(testDataView.indexPattern, testDataView.name, testDataView.id); + + deleteSavedQueries(); + createSavedQuery(testSavedQuery.name, testSavedQuery.query, testSavedQuery.filterKey) + .its('body.id') + .then((id: string) => { + (SAVED_QUERY_DATA_VIEW_RULE['security-rule'] as { saved_id: string }).saved_id = id; + + installPrebuiltRuleAssets([ + CUSTOM_QUERY_INDEX_PATTERN_RULE, + SAVED_QUERY_DATA_VIEW_RULE, + MACHINE_LEARNING_RULE, + THRESHOLD_RULE_INDEX_PATTERN, + EQL_INDEX_PATTERN_RULE, + THREAT_MATCH_INDEX_PATTERN_RULE, + NEW_TERMS_INDEX_PATTERN_RULE, + ESQL_RULE, + RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES, + ]); + }); + + fetchMachineLearningModules() + .its('body') + .then((mlModules) => { + cy.wrap(mlModules).as('mlModules'); + }); + }); + it('Custom query rule properties', () => { clickAddElasticRulesButton(); @@ -661,216 +661,215 @@ describe('Detection rules, Prebuilt Rules Installation and Update workflow', () }); }); }); - }); - - describe('Upgrade of prebuilt rules', () => { - const RULE_1_ID = 'rule_1'; - const RULE_2_ID = 'rule_2'; - const OUTDATED_RULE_1 = createRuleAssetSavedObject({ - name: 'Outdated rule 1', - rule_id: RULE_1_ID, - version: 1, - }); - const UPDATED_RULE_1 = createRuleAssetSavedObject({ - name: 'Updated rule 1', - rule_id: RULE_1_ID, - version: 2, - }); - const OUTDATED_RULE_2 = createRuleAssetSavedObject({ - name: 'Outdated rule 2', - rule_id: RULE_2_ID, - version: 1, - }); - const UPDATED_RULE_2 = createRuleAssetSavedObject({ - name: 'Updated rule 2', - rule_id: RULE_2_ID, - version: 2, - }); - beforeEach(() => { - cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_perform').as( - 'updatePrebuiltRules' - ); - /* Create a new rule and install it */ - createAndInstallMockedPrebuiltRules([OUTDATED_RULE_1, OUTDATED_RULE_2]); - /* Create a second version of the rule, making it available for update */ - installPrebuiltRuleAssets([UPDATED_RULE_1, UPDATED_RULE_2]); - visitRulesManagementTable(); - }); + describe('Upgrade of prebuilt rules', () => { + const RULE_1_ID = 'rule_1'; + const RULE_2_ID = 'rule_2'; + const OUTDATED_RULE_1 = createRuleAssetSavedObject({ + name: 'Outdated rule 1', + rule_id: RULE_1_ID, + version: 1, + }); + const UPDATED_RULE_1 = createRuleAssetSavedObject({ + name: 'Updated rule 1', + rule_id: RULE_1_ID, + version: 2, + }); + const OUTDATED_RULE_2 = createRuleAssetSavedObject({ + name: 'Outdated rule 2', + rule_id: RULE_2_ID, + version: 1, + }); + const UPDATED_RULE_2 = createRuleAssetSavedObject({ + name: 'Updated rule 2', + rule_id: RULE_2_ID, + version: 2, + }); + beforeEach(() => { + cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_perform').as( + 'updatePrebuiltRules' + ); + /* Create a new rule and install it */ + createAndInstallMockedPrebuiltRules([OUTDATED_RULE_1, OUTDATED_RULE_2]); + /* Create a second version of the rule, making it available for update */ + installPrebuiltRuleAssets([UPDATED_RULE_1, UPDATED_RULE_2]); + + visitRulesManagementTable(); + }); - describe('Basic functionality', { tags: TEST_ENV_TAGS }, () => { - it('User can preview rules available for upgrade', () => { - clickRuleUpdatesTab(); + describe('Basic functionality', () => { + it('User can preview rules available for upgrade', () => { + clickRuleUpdatesTab(); - openRuleUpdatePreview(OUTDATED_RULE_1['security-rule'].name); - cy.get(UPDATE_PREBUILT_RULE_BUTTON).click(); - cy.wait('@updatePrebuiltRules'); + openRuleUpdatePreview(OUTDATED_RULE_1['security-rule'].name); + cy.get(UPDATE_PREBUILT_RULE_BUTTON).click(); + cy.wait('@updatePrebuiltRules'); - assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_1]); - cy.get(RULE_MANAGEMENT_PAGE_BREADCRUMB).click(); + assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_1]); + cy.get(RULE_MANAGEMENT_PAGE_BREADCRUMB).click(); - assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_1]); - }); + assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_1]); + }); + + it('User can upgrade a rule using the rule preview', () => { + clickRuleUpdatesTab(); + + openRuleUpdatePreview(OUTDATED_RULE_1['security-rule'].name); + closeRulePreview(); + }); + + it('Tabs and sections without content should be hidden in preview before upgrading', () => { + const UPDATED_RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES = { + ...RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES, + ['security-rule']: { + ...RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES['security-rule'], + version: 2, + }, + }; + + createAndInstallMockedPrebuiltRules([RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES]); + + installPrebuiltRuleAssets([UPDATED_RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES]); + + cy.reload(); - it('User can upgrade a rule using the rule preview', () => { - clickRuleUpdatesTab(); + clickRuleUpdatesTab(); - openRuleUpdatePreview(OUTDATED_RULE_1['security-rule'].name); - closeRulePreview(); + openRuleUpdatePreview( + UPDATED_RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES['security-rule'].name + ); + cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Investigation guide').should('not.exist'); + cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Setup guide').should('not.exist'); + }); }); - it('Tabs and sections without content should be hidden in preview before upgrading', () => { - const UPDATED_RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES = { - ...RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES, + describe('User can see correct rule information in preview before upgrading', () => { + const UPDATED_CUSTOM_QUERY_INDEX_PATTERN_RULE = { + ...CUSTOM_QUERY_INDEX_PATTERN_RULE, ['security-rule']: { - ...RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES['security-rule'], + ...CUSTOM_QUERY_INDEX_PATTERN_RULE['security-rule'], + query: '_id : * and event.type: start', version: 2, }, }; - createAndInstallMockedPrebuiltRules([RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES]); - - installPrebuiltRuleAssets([UPDATED_RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES]); + const UPDATED_SAVED_QUERY_DATA_VIEW_RULE = { + ...SAVED_QUERY_DATA_VIEW_RULE, + ['security-rule']: { + ...SAVED_QUERY_DATA_VIEW_RULE['security-rule'], + alert_suppression: { + group_by: ['Endpoint.policy.applied.id'], + duration: { unit: 'm', value: 10 }, + missing_fields_strategy: 'suppress', + }, + version: 2, + }, + } as typeof SAVED_QUERY_DATA_VIEW_RULE; - cy.reload(); + const UPDATED_MACHINE_LEARNING_RULE = { + ...MACHINE_LEARNING_RULE, + ['security-rule']: { + ...MACHINE_LEARNING_RULE['security-rule'], + anomaly_threshold: 99, + version: 2, + }, + }; - clickRuleUpdatesTab(); + const UPDATED_THRESHOLD_RULE_INDEX_PATTERN = { + ...THRESHOLD_RULE_INDEX_PATTERN, + ['security-rule']: { + ...THRESHOLD_RULE_INDEX_PATTERN['security-rule'], + threshold: { + field: ['Endpoint.policy.applied.artifacts.user.identifiers.name'], + value: 999, + cardinality: [{ field: 'Ransomware.score', value: 10 }], + }, + version: 2, + }, + }; - openRuleUpdatePreview( - UPDATED_RULE_WITHOUT_INVESTIGATION_AND_SETUP_GUIDES['security-rule'].name - ); - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Investigation guide').should('not.exist'); - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Setup guide').should('not.exist'); - }); - }); + const UPDATED_EQL_INDEX_PATTERN_RULE = { + ...EQL_INDEX_PATTERN_RULE, + ['security-rule']: { + ...EQL_INDEX_PATTERN_RULE['security-rule'], + query: 'process where process.name == "regsvr32.exe" and process.pid == 1234', + version: 2, + }, + }; - describe('User can see correct rule information in preview before upgrading', () => { - const UPDATED_CUSTOM_QUERY_INDEX_PATTERN_RULE = { - ...CUSTOM_QUERY_INDEX_PATTERN_RULE, - ['security-rule']: { - ...CUSTOM_QUERY_INDEX_PATTERN_RULE['security-rule'], - query: '_id : * and event.type: start', - version: 2, - }, - }; - - const UPDATED_SAVED_QUERY_DATA_VIEW_RULE = { - ...SAVED_QUERY_DATA_VIEW_RULE, - ['security-rule']: { - ...SAVED_QUERY_DATA_VIEW_RULE['security-rule'], - alert_suppression: { - group_by: ['Endpoint.policy.applied.id'], - duration: { unit: 'm', value: 10 }, - missing_fields_strategy: 'suppress', + const UPDATED_THREAT_MATCH_INDEX_PATTERN_RULE = { + ...THREAT_MATCH_INDEX_PATTERN_RULE, + ['security-rule']: { + ...THREAT_MATCH_INDEX_PATTERN_RULE['security-rule'], + threat_query: '@timestamp >= "now-60d/d"', + version: 2, }, - version: 2, - }, - } as typeof SAVED_QUERY_DATA_VIEW_RULE; - - const UPDATED_MACHINE_LEARNING_RULE = { - ...MACHINE_LEARNING_RULE, - ['security-rule']: { - ...MACHINE_LEARNING_RULE['security-rule'], - anomaly_threshold: 99, - version: 2, - }, - }; - - const UPDATED_THRESHOLD_RULE_INDEX_PATTERN = { - ...THRESHOLD_RULE_INDEX_PATTERN, - ['security-rule']: { - ...THRESHOLD_RULE_INDEX_PATTERN['security-rule'], - threshold: { - field: ['Endpoint.policy.applied.artifacts.user.identifiers.name'], - value: 999, - cardinality: [{ field: 'Ransomware.score', value: 10 }], + }; + + const UPDATED_NEW_TERMS_INDEX_PATTERN_RULE = { + ...NEW_TERMS_INDEX_PATTERN_RULE, + ['security-rule']: { + ...NEW_TERMS_INDEX_PATTERN_RULE['security-rule'], + new_terms_fields: ['Endpoint.policy.applied.id', 'Memory_protection.unique_key_v2'], + history_window_start: 'now-10d', + version: 2, }, - version: 2, - }, - }; - - const UPDATED_EQL_INDEX_PATTERN_RULE = { - ...EQL_INDEX_PATTERN_RULE, - ['security-rule']: { - ...EQL_INDEX_PATTERN_RULE['security-rule'], - query: 'process where process.name == "regsvr32.exe" and process.pid == 1234', - version: 2, - }, - }; - - const UPDATED_THREAT_MATCH_INDEX_PATTERN_RULE = { - ...THREAT_MATCH_INDEX_PATTERN_RULE, - ['security-rule']: { - ...THREAT_MATCH_INDEX_PATTERN_RULE['security-rule'], - threat_query: '@timestamp >= "now-60d/d"', - version: 2, - }, - }; - - const UPDATED_NEW_TERMS_INDEX_PATTERN_RULE = { - ...NEW_TERMS_INDEX_PATTERN_RULE, - ['security-rule']: { - ...NEW_TERMS_INDEX_PATTERN_RULE['security-rule'], - new_terms_fields: ['Endpoint.policy.applied.id', 'Memory_protection.unique_key_v2'], - history_window_start: 'now-10d', - version: 2, - }, - }; - - const UPDATED_ESQL_RULE = { - ...ESQL_RULE, - ['security-rule']: { - ...ESQL_RULE['security-rule'], - query: - 'FROM .alerts-security.alerts-default | STATS count = COUNT(@timestamp) BY @timestamp, event.category', - version: 2, - }, - }; + }; - beforeEach(() => { - deleteDataView(testDataView.id); - postDataView(testDataView.indexPattern, testDataView.name, testDataView.id); - - deleteSavedQueries(); - createSavedQuery(testSavedQuery.name, testSavedQuery.query, testSavedQuery.filterKey) - .its('body.id') - .then((id: string) => { - (UPDATED_SAVED_QUERY_DATA_VIEW_RULE['security-rule'] as { saved_id: string }).saved_id = - id; - - createAndInstallMockedPrebuiltRules([ - CUSTOM_QUERY_INDEX_PATTERN_RULE, - SAVED_QUERY_DATA_VIEW_RULE, - MACHINE_LEARNING_RULE, - THRESHOLD_RULE_INDEX_PATTERN, - EQL_INDEX_PATTERN_RULE, - THREAT_MATCH_INDEX_PATTERN_RULE, - NEW_TERMS_INDEX_PATTERN_RULE, - ESQL_RULE, - ]); - - installPrebuiltRuleAssets([ - UPDATED_CUSTOM_QUERY_INDEX_PATTERN_RULE, - UPDATED_SAVED_QUERY_DATA_VIEW_RULE, - UPDATED_MACHINE_LEARNING_RULE, - UPDATED_THRESHOLD_RULE_INDEX_PATTERN, - UPDATED_EQL_INDEX_PATTERN_RULE, - UPDATED_THREAT_MATCH_INDEX_PATTERN_RULE, - UPDATED_NEW_TERMS_INDEX_PATTERN_RULE, - UPDATED_ESQL_RULE, - ]); - - cy.reload(); - }); + const UPDATED_ESQL_RULE = { + ...ESQL_RULE, + ['security-rule']: { + ...ESQL_RULE['security-rule'], + query: + 'FROM .alerts-security.alerts-default | STATS count = COUNT(@timestamp) BY @timestamp, event.category', + version: 2, + }, + }; - fetchMachineLearningModules() - .its('body') - .then((mlModules) => { - cy.wrap(mlModules).as('mlModules'); - }); - }); + beforeEach(() => { + deleteDataView(testDataView.id); + postDataView(testDataView.indexPattern, testDataView.name, testDataView.id); + + deleteSavedQueries(); + createSavedQuery(testSavedQuery.name, testSavedQuery.query, testSavedQuery.filterKey) + .its('body.id') + .then((id: string) => { + ( + UPDATED_SAVED_QUERY_DATA_VIEW_RULE['security-rule'] as { saved_id: string } + ).saved_id = id; + + createAndInstallMockedPrebuiltRules([ + CUSTOM_QUERY_INDEX_PATTERN_RULE, + SAVED_QUERY_DATA_VIEW_RULE, + MACHINE_LEARNING_RULE, + THRESHOLD_RULE_INDEX_PATTERN, + EQL_INDEX_PATTERN_RULE, + THREAT_MATCH_INDEX_PATTERN_RULE, + NEW_TERMS_INDEX_PATTERN_RULE, + ESQL_RULE, + ]); + + installPrebuiltRuleAssets([ + UPDATED_CUSTOM_QUERY_INDEX_PATTERN_RULE, + UPDATED_SAVED_QUERY_DATA_VIEW_RULE, + UPDATED_MACHINE_LEARNING_RULE, + UPDATED_THRESHOLD_RULE_INDEX_PATTERN, + UPDATED_EQL_INDEX_PATTERN_RULE, + UPDATED_THREAT_MATCH_INDEX_PATTERN_RULE, + UPDATED_NEW_TERMS_INDEX_PATTERN_RULE, + UPDATED_ESQL_RULE, + ]); + + cy.reload(); + }); + + fetchMachineLearningModules() + .its('body') + .then((mlModules) => { + cy.wrap(mlModules).as('mlModules'); + }); + }); - describe('All environments', { tags: TEST_ENV_TAGS }, () => { it('Custom query rule properties', () => { clickRuleUpdatesTab(); @@ -1087,166 +1086,138 @@ describe('Detection rules, Prebuilt Rules Installation and Update workflow', () assertAlertSuppressionPropertiesShown(alertSuppression); }); }); - }); - - describe('Viewing rule changes in JSON diff view', { tags: TEST_ENV_TAGS }, () => { - it('User can see changes in a side-by-side JSON diff view', () => { - clickRuleUpdatesTab(); - - openRuleUpdatePreview(OUTDATED_RULE_1['security-rule'].name); - selectPreviewTab(PREVIEW_TABS.JSON_VIEW); - - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Current rule').should('be.visible'); - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Elastic update').should('be.visible'); - - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('"version": 1').should('be.visible'); - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('"version": 2').should('be.visible'); - - cy.get(UPDATE_PREBUILT_RULE_PREVIEW) - .contains('"name": "Outdated rule 1"') - .should('be.visible'); - /* Select another rule without closing the preview for the current rule */ - openRuleUpdatePreview(OUTDATED_RULE_2['security-rule'].name); + describe('Viewing rule changes in JSON diff view', () => { + it('User can see changes in a side-by-side JSON diff view', () => { + clickRuleUpdatesTab(); - /* Make sure the JSON diff is displayed for the newly selected rule */ - cy.get(UPDATE_PREBUILT_RULE_PREVIEW) - .contains('"name": "Outdated rule 2"') - .should('be.visible'); - cy.get(UPDATE_PREBUILT_RULE_PREVIEW) - .contains('"name": "Outdated rule 1"') - .should('not.exist'); - }); + openRuleUpdatePreview(OUTDATED_RULE_1['security-rule'].name); + selectPreviewTab(PREVIEW_TABS.JSON_VIEW); - it('Dynamic properties should not be included in preview', () => { - const dateBeforeRuleExecution = new Date(); + cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Current rule').should('be.visible'); + cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Elastic update').should('be.visible'); - /* Enable a rule and wait for it to execute */ - enableRules({ names: [OUTDATED_RULE_1['security-rule'].name] }); - waitForRulesToFinishExecution( - [OUTDATED_RULE_1['security-rule'].rule_id], - dateBeforeRuleExecution - ); + cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('"version": 1').should('be.visible'); + cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('"version": 2').should('be.visible'); - cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_review').as( - 'updatePrebuiltRulesReview' - ); + cy.get(UPDATE_PREBUILT_RULE_PREVIEW) + .contains('"name": "Outdated rule 1"') + .should('be.visible'); - clickRuleUpdatesTab(); + closeRulePreview(); - /* Check that API response contains dynamic properties, like "enabled" and "execution_summary" */ - cy.wait('@updatePrebuiltRulesReview') - .its('response.body') - .then((body: ReviewRuleUpgradeResponseBody) => { - const executedRuleInfo = body.rules.find( - (ruleInfo) => ruleInfo.rule_id === OUTDATED_RULE_1['security-rule'].rule_id - ); + openRuleUpdatePreview(OUTDATED_RULE_2['security-rule'].name); + selectPreviewTab(PREVIEW_TABS.JSON_VIEW); - const enabled = executedRuleInfo?.current_rule?.enabled; - expect(enabled).to.eql(true); + /* Make sure the JSON diff is displayed for the newly selected rule */ + cy.get(UPDATE_PREBUILT_RULE_PREVIEW) + .contains('"name": "Outdated rule 2"') + .should('be.visible'); + cy.get(UPDATE_PREBUILT_RULE_PREVIEW) + .contains('"name": "Outdated rule 1"') + .should('not.exist'); + }); - const executionSummary = executedRuleInfo?.current_rule?.execution_summary; - expect(executionSummary).to.not.eql(undefined); - }); + it('Dynamic properties should not be included in preview', () => { + const dateBeforeRuleExecution = new Date(); - /* Open the preview and check that dynamic properties are not shown in the diff */ - openRuleUpdatePreview(OUTDATED_RULE_1['security-rule'].name); + /* Enable a rule and wait for it to execute */ + enableRules({ names: [OUTDATED_RULE_1['security-rule'].name] }); + waitForRulesToFinishExecution( + [OUTDATED_RULE_1['security-rule'].rule_id], + dateBeforeRuleExecution + ); - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('enabled').should('not.exist'); - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('execution_summary').should('not.exist'); - }); - }); + cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_review').as( + 'updatePrebuiltRulesReview' + ); - describe('Viewing rule changes in per-field diff view', { tags: TEST_ENV_TAGS }, () => { - it('User can see changes in a side-by-side per-field diff view', () => { - clickRuleUpdatesTab(); + clickRuleUpdatesTab(); - openRuleUpdatePreview(OUTDATED_RULE_1['security-rule'].name); - assertSelectedPreviewTab(PREVIEW_TABS.UPDATES); // Should be open by default + /* Check that API response contains dynamic properties, like "enabled" and "execution_summary" */ + cy.wait('@updatePrebuiltRulesReview') + .its('response.body') + .then((body: ReviewRuleUpgradeResponseBody) => { + const executedRuleInfo = body.rules.find( + (ruleInfo) => ruleInfo.rule_id === OUTDATED_RULE_1['security-rule'].rule_id + ); - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Current rule').should('be.visible'); - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Elastic update').should('be.visible'); + const enabled = executedRuleInfo?.current_rule?.enabled; + expect(enabled).to.eql(true); - cy.get(PER_FIELD_DIFF_WRAPPER).should('have.length', 2); + const executionSummary = executedRuleInfo?.current_rule?.execution_summary; + expect(executionSummary).to.not.eql(undefined); + }); - /* Version should be the first field in the order */ - cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('Version').should('be.visible'); - cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('1').should('be.visible'); - cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('2').should('be.visible'); + /* Open the preview and check that dynamic properties are not shown in the diff */ + openRuleUpdatePreview(OUTDATED_RULE_1['security-rule'].name); - cy.get(PER_FIELD_DIFF_WRAPPER).last().contains('Name').should('be.visible'); - cy.get(PER_FIELD_DIFF_WRAPPER).last().contains('Outdated rule 1').should('be.visible'); - cy.get(PER_FIELD_DIFF_WRAPPER).last().contains('Updated rule 1').should('be.visible'); + cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('enabled').should('not.exist'); + cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('execution_summary').should('not.exist'); + }); }); - it('User can switch between rules upgrades without closing flyout', () => { - clickRuleUpdatesTab(); - - openRuleUpdatePreview(OUTDATED_RULE_1['security-rule'].name); - assertSelectedPreviewTab(PREVIEW_TABS.UPDATES); // Should be open by default - - /* Version should be the first field in the order */ - cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('Version').should('be.visible'); - cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('1').should('be.visible'); - cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('2').should('be.visible'); + describe('Viewing rule changes in per-field diff view', () => { + it('User can see changes in a side-by-side per-field diff view', () => { + clickRuleUpdatesTab(); - cy.get(PER_FIELD_DIFF_WRAPPER).last().contains('Name').should('be.visible'); - cy.get(PER_FIELD_DIFF_WRAPPER).last().contains('Outdated rule 1').should('be.visible'); - cy.get(PER_FIELD_DIFF_WRAPPER).last().contains('Updated rule 1').should('be.visible'); + openRuleUpdatePreview(OUTDATED_RULE_1['security-rule'].name); + assertSelectedPreviewTab(PREVIEW_TABS.UPDATES); // Should be open by default - /* Select another rule without closing the preview for the current rule */ - openRuleUpdatePreview(OUTDATED_RULE_2['security-rule'].name); + cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Current rule').should('be.visible'); + cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Elastic update').should('be.visible'); - /* Make sure the per-field diff is displayed for the newly selected rule */ - cy.get(PER_FIELD_DIFF_WRAPPER).last().contains('Name').should('be.visible'); - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Outdated rule 2').should('be.visible'); - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Updated rule 2').should('be.visible'); - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Outdated rule 1').should('not.exist'); - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Updated rule 1').should('not.exist'); + cy.get(PER_FIELD_DIFF_WRAPPER).should('have.length', 2); - cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('Version').should('be.visible'); - cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('1').should('be.visible'); - cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('2').should('be.visible'); - }); + /* Version should be the first field in the order */ + cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('Version').should('be.visible'); + cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('1').should('be.visible'); + cy.get(PER_FIELD_DIFF_WRAPPER).first().contains('2').should('be.visible'); - it('User can see changes when updated rule is a different rule type', () => { - const OUTDATED_RULE_WITH_QUERY_TYPE = createRuleAssetSavedObject({ - name: 'Query rule', - rule_id: 'rule_id', - version: 1, - type: 'query', - language: 'kuery', + cy.get(PER_FIELD_DIFF_WRAPPER).last().contains('Name').should('be.visible'); + cy.get(PER_FIELD_DIFF_WRAPPER).last().contains('Outdated rule 1').should('be.visible'); + cy.get(PER_FIELD_DIFF_WRAPPER).last().contains('Updated rule 1').should('be.visible'); }); - const UPDATED_RULE_WITH_EQL_TYPE = createRuleAssetSavedObject({ - language: 'eql', - name: 'EQL rule', - rule_id: 'rule_id', - version: 2, - type: 'eql', - }); - /* Create a new rule and install it */ - createAndInstallMockedPrebuiltRules([OUTDATED_RULE_WITH_QUERY_TYPE]); - /* Create a second version of the rule, making it available for update */ - installPrebuiltRuleAssets([UPDATED_RULE_WITH_EQL_TYPE]); - cy.reload(); - clickRuleUpdatesTab(); + it('User can see changes when updated rule is a different rule type', () => { + const OUTDATED_RULE_WITH_QUERY_TYPE = createRuleAssetSavedObject({ + name: 'Query rule', + rule_id: 'rule_id', + version: 1, + type: 'query', + language: 'kuery', + }); + const UPDATED_RULE_WITH_EQL_TYPE = createRuleAssetSavedObject({ + language: 'eql', + name: 'EQL rule', + rule_id: 'rule_id', + version: 2, + type: 'eql', + }); + /* Create a new rule and install it */ + createAndInstallMockedPrebuiltRules([OUTDATED_RULE_WITH_QUERY_TYPE]); + /* Create a second version of the rule, making it available for update */ + installPrebuiltRuleAssets([UPDATED_RULE_WITH_EQL_TYPE]); - openRuleUpdatePreview(OUTDATED_RULE_WITH_QUERY_TYPE['security-rule'].name); - assertSelectedPreviewTab(PREVIEW_TABS.UPDATES); // Should be open by default + cy.reload(); + clickRuleUpdatesTab(); + + openRuleUpdatePreview(OUTDATED_RULE_WITH_QUERY_TYPE['security-rule'].name); + assertSelectedPreviewTab(PREVIEW_TABS.UPDATES); // Should be open by default - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Current rule').should('be.visible'); - cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Elastic update').should('be.visible'); + cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Current rule').should('be.visible'); + cy.get(UPDATE_PREBUILT_RULE_PREVIEW).contains('Elastic update').should('be.visible'); - cy.get(PER_FIELD_DIFF_WRAPPER).should('have.length', 5); + cy.get(PER_FIELD_DIFF_WRAPPER).should('have.length', 5); - cy.get(PER_FIELD_DIFF_DEFINITION_SECTION).contains('Type').should('be.visible'); - cy.get(PER_FIELD_DIFF_DEFINITION_SECTION).contains('query').should('be.visible'); - cy.get(PER_FIELD_DIFF_DEFINITION_SECTION).contains('eql').should('be.visible'); + cy.get(PER_FIELD_DIFF_DEFINITION_SECTION).contains('Type').should('be.visible'); + cy.get(PER_FIELD_DIFF_DEFINITION_SECTION).contains('query').should('be.visible'); + cy.get(PER_FIELD_DIFF_DEFINITION_SECTION).contains('eql').should('be.visible'); - cy.get(PER_FIELD_DIFF_DEFINITION_SECTION).contains('KQL query').should('exist'); - cy.get(PER_FIELD_DIFF_DEFINITION_SECTION).contains('EQL query').should('exist'); + cy.get(PER_FIELD_DIFF_DEFINITION_SECTION).contains('KQL query').should('exist'); + cy.get(PER_FIELD_DIFF_DEFINITION_SECTION).contains('EQL query').should('exist'); + }); }); }); - }); -}); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/update_workflow.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/update_workflow.cy.ts similarity index 100% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/update_workflow.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/update_workflow.cy.ts diff --git a/x-pack/test/security_solution_cypress/cypress/screens/alerts_detection_rules.ts b/x-pack/test/security_solution_cypress/cypress/screens/alerts_detection_rules.ts index 2f59e9362f051..bbc7a346b252f 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/alerts_detection_rules.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/alerts_detection_rules.ts @@ -280,7 +280,7 @@ export const CUSTOM_QUERY_VALUE = '[data-test-subj="customQueryPropertyValue"]'; export const FILTERS_TITLE = '[data-test-subj="filtersPropertyTitle"]'; export const FILTERS_VALUE_ITEM = - '[data-test-subj="filtersPropertyValue"] [data-test-subj^="filterItem-"]'; + '[data-test-subj="filtersPropertyValue"] [data-test-subj*="filter-badge-"]'; export const ALERT_SUPPRESSION_GROUP_BY_TITLE = '[data-test-subj="alertSuppressionGroupByPropertyTitle"]'; @@ -308,7 +308,7 @@ export const SAVED_QUERY_CONTENT_VALUE = '[data-test-subj="savedQueryContentProp export const SAVED_QUERY_FILTERS_TITLE = '[data-test-subj="savedQueryFiltersPropertyTitle"]'; export const SAVED_QUERY_FILTERS_VALUE = - '[data-test-subj="savedQueryFiltersPropertyValue"] [data-test-subj^="filterItem-"]'; + '[data-test-subj="savedQueryFiltersPropertyValue"] [data-test-subj*="filter-badge-"]'; export const SAVED_QUERY_NAME_TITLE = '[data-test-subj="savedQueryNamePropertyTitle"]'; export const SAVED_QUERY_NAME_VALUE = '[data-test-subj="savedQueryNamePropertyValue"]'; @@ -333,7 +333,7 @@ export const THREAT_MAPPING_VALUE = '[data-test-subj="threatMappingPropertyValue export const THREAT_FILTERS_TITLE = '[data-test-subj="threatFiltersPropertyTitle"]'; export const THREAT_FILTERS_VALUE_ITEM = - '[data-test-subj="threatFiltersPropertyValue"] [data-test-subj^="filterItem-"]'; + '[data-test-subj="threatFiltersPropertyValue"] [data-test-subj*="filter-badge-"]'; export const THREAT_QUERY_TITLE = '[data-test-subj="threatQueryPropertyTitle"]'; export const THREAT_QUERY_VALUE = '[data-test-subj="threatQueryPropertyValue"]'; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/prebuilt_rules_preview.ts b/x-pack/test/security_solution_cypress/cypress/tasks/prebuilt_rules_preview.ts index 9ce0a1cd84bb6..6617b10ccc219 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/prebuilt_rules_preview.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/prebuilt_rules_preview.ts @@ -222,7 +222,7 @@ export const assertCommonPropertiesShown = (properties: Partial Date: Fri, 13 Sep 2024 08:03:31 -0400 Subject: [PATCH 11/17] [Fleet] Add actionable error message when generating logstash API keys (#192765) --- .../plugins/fleet/common/constants/output.ts | 15 ++++ .../components/logstash_instructions/hooks.ts | 47 ---------- .../logstash_instructions/hooks.tsx | 88 +++++++++++++++++++ .../services/api_keys/logstash_api_keys.ts | 21 ++--- 4 files changed, 112 insertions(+), 59 deletions(-) delete mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/hooks.ts create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/hooks.tsx diff --git a/x-pack/plugins/fleet/common/constants/output.ts b/x-pack/plugins/fleet/common/constants/output.ts index fb01ba991d3d2..5f6b247a6b289 100644 --- a/x-pack/plugins/fleet/common/constants/output.ts +++ b/x-pack/plugins/fleet/common/constants/output.ts @@ -139,3 +139,18 @@ export const OUTPUT_TYPES_WITH_PRESET_SUPPORT: Array> = [ ]; export const OUTPUT_HEALTH_DATA_STREAM = 'logs-fleet_server.output_health-default'; + +export const LOGSTASH_API_KEY_CLUSTER_PERMISSIONS = ['monitor', 'manage_own_api_key']; + +export const LOGSTASH_API_KEY_INDICES_PRIVILEGES = ['auto_configure', 'create_doc']; + +export const LOGSTASH_API_KEY_INDICES = [ + 'logs-*-*', + 'metrics-*-*', + 'traces-*-*', + 'synthetics-*-*', + '.logs-endpoint.diagnostic.collection-*', + '.logs-endpoint.action.responses-*', + 'profiling-*', + '.profiling-*', +]; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/hooks.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/hooks.ts deleted file mode 100644 index 228140ac290b6..0000000000000 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/hooks.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 { useState, useMemo, useCallback } from 'react'; -import { i18n } from '@kbn/i18n'; - -import { sendPostLogstashApiKeys, useStartServices } from '../../../../hooks'; - -export function useLogstashApiKey() { - const [isLoading, setIsLoading] = useState(false); - const [apiKey, setApiKey] = useState(); - const { notifications } = useStartServices(); - - const generateApiKey = useCallback(async () => { - try { - setIsLoading(true); - - const res = await sendPostLogstashApiKeys(); - if (res.error) { - throw res.error; - } - - setApiKey(res.data?.api_key); - } catch (err) { - notifications.toasts.addError(err, { - title: i18n.translate('xpack.fleet.settings.logstashInstructions.generateApiKeyError', { - defaultMessage: 'Impossible to generate an api key', - }), - }); - } finally { - setIsLoading(false); - } - }, [notifications.toasts]); - - return useMemo( - () => ({ - isLoading, - generateApiKey, - apiKey, - }), - [isLoading, generateApiKey, apiKey] - ); -} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/hooks.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/hooks.tsx new file mode 100644 index 0000000000000..78e8b930b45aa --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/hooks.tsx @@ -0,0 +1,88 @@ +/* + * 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 React, { useState, useMemo, useCallback } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiCode } from '@elastic/eui'; +import { toMountPoint } from '@kbn/react-kibana-mount'; + +import { + LOGSTASH_API_KEY_CLUSTER_PERMISSIONS, + LOGSTASH_API_KEY_INDICES, + LOGSTASH_API_KEY_INDICES_PRIVILEGES, +} from '../../../../../../../common/constants'; +import { sendPostLogstashApiKeys, useStartServices } from '../../../../hooks'; + +export function useLogstashApiKey() { + const [isLoading, setIsLoading] = useState(false); + const [apiKey, setApiKey] = useState(); + const startServices = useStartServices(); + const { notifications } = startServices; + const generateApiKey = useCallback(async () => { + try { + setIsLoading(true); + + const res = await sendPostLogstashApiKeys(); + if (res.error) { + throw res.error; + } + + setApiKey(res.data?.api_key); + } catch (err) { + if (err.statusCode === 403) { + notifications.toasts.addDanger( + { + title: i18n.translate('xpack.fleet.settings.logstashInstructions.generateApiKeyError', { + defaultMessage: 'Cannot generate an API key', + }), + text: toMountPoint( + {LOGSTASH_API_KEY_CLUSTER_PERMISSIONS.join(', ')} + ), + indexPermissions: ( + {LOGSTASH_API_KEY_INDICES_PRIVILEGES.join(', ')} + ), + indexes: LOGSTASH_API_KEY_INDICES.map((index) => ( + + {index} +
+
+ )), + br:
, + }} + />, + startServices + ), + }, + {} + ); + } else { + notifications.toasts.addError(err, { + title: i18n.translate('xpack.fleet.settings.logstashInstructions.generateApiKeyError', { + defaultMessage: 'Cannot generate an API key', + }), + }); + } + } finally { + setIsLoading(false); + } + }, [notifications.toasts, startServices]); + + return useMemo( + () => ({ + isLoading, + generateApiKey, + apiKey, + }), + [isLoading, generateApiKey, apiKey] + ); +} diff --git a/x-pack/plugins/fleet/server/services/api_keys/logstash_api_keys.ts b/x-pack/plugins/fleet/server/services/api_keys/logstash_api_keys.ts index 66888223b02d1..cf6a68f5c0271 100644 --- a/x-pack/plugins/fleet/server/services/api_keys/logstash_api_keys.ts +++ b/x-pack/plugins/fleet/server/services/api_keys/logstash_api_keys.ts @@ -7,6 +7,12 @@ import type { ElasticsearchClient } from '@kbn/core/server'; +import { + LOGSTASH_API_KEY_CLUSTER_PERMISSIONS, + LOGSTASH_API_KEY_INDICES, + LOGSTASH_API_KEY_INDICES_PRIVILEGES, +} from '../../../common/constants'; + /** * Check if an esClient has enought permission to create a valid API key for logstash * @@ -14,20 +20,11 @@ import type { ElasticsearchClient } from '@kbn/core/server'; */ export async function canCreateLogstashApiKey(esClient: ElasticsearchClient) { const res = await esClient.security.hasPrivileges({ - cluster: ['monitor', 'manage_own_api_key'], + cluster: LOGSTASH_API_KEY_CLUSTER_PERMISSIONS, index: [ { - names: [ - 'logs-*-*', - 'metrics-*-*', - 'traces-*-*', - 'synthetics-*-*', - '.logs-endpoint.diagnostic.collection-*', - '.logs-endpoint.action.responses-*', - 'profiling-*', - '.profiling-*', - ], - privileges: ['auto_configure', 'create_doc'], + names: LOGSTASH_API_KEY_INDICES, + privileges: LOGSTASH_API_KEY_INDICES_PRIVILEGES, }, ], }); From 48bbdd8a576522881e1af452cfaca8e529c258fa Mon Sep 17 00:00:00 2001 From: Katerina Date: Fri, 13 Sep 2024 15:05:51 +0300 Subject: [PATCH 12/17] [APM][ECO] Remove `log.level` filter from log rate metric collection (#192500) ## Summary closes https://github.com/elastic/kibana/issues/189923 - EEM: remove `log.level` filter and filter based on `data_stream.type: logs` - log-data-access: remove `log.level` filter - ECO: Update the formula and Explore log filter and remove the N/A badge https://github.com/user-attachments/assets/9b190ba7-c28b-44cf-bb0b-6dbdfe47ced4 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../app/entities/charts/log_rate_chart.tsx | 6 +-- .../table/get_service_columns.tsx | 11 +--- .../charts/helper/get_metrics_formulas.tsx | 2 +- .../not_available_log_metrics.tsx | 53 ------------------- .../server/lib/entities/built_in/services.ts | 4 +- .../get_logs_error_rate_timeseries.ts | 3 +- .../get_logs_rate_timeseries.ts | 4 +- 7 files changed, 7 insertions(+), 76 deletions(-) delete mode 100644 x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_log_metrics.tsx diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/entities/charts/log_rate_chart.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/entities/charts/log_rate_chart.tsx index 413fad8cdf3d3..b27d73004d489 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/entities/charts/log_rate_chart.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/entities/charts/log_rate_chart.tsx @@ -121,11 +121,7 @@ export function LogRateChart({ height }: { height: number }) {
- +
diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx index 3cfa4223cefdb..ec6385b37dee3 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx @@ -36,10 +36,9 @@ import { NotAvailableApmMetrics } from '../../../../shared/not_available_popover import { TruncateWithTooltip } from '../../../../shared/truncate_with_tooltip'; import { ServiceInventoryFieldName } from './multi_signal_services_table'; import { EntityDataStreamType } from '../../../../../../common/entities/types'; -import { isApmSignal, isLogsSignal } from '../../../../../utils/get_signal_type'; +import { isApmSignal } from '../../../../../utils/get_signal_type'; import { ColumnHeader } from './column_header'; import { APIReturnType } from '../../../../../services/rest/create_call_apm_api'; -import { NotAvailableLogsMetrics } from '../../../../shared/not_available_popover/not_available_log_metrics'; type ServicesDetailedStatisticsAPIResponse = APIReturnType<'POST /internal/apm/entities/services/detailed_statistics'>; @@ -204,10 +203,6 @@ export function getServiceColumns({ dataType: 'number', align: RIGHT_ALIGNMENT, render: (_, { metrics, serviceName, dataStreamTypes, hasLogMetrics }) => { - if (isLogsSignal(dataStreamTypes) && !hasLogMetrics) { - return ; - } - const { currentPeriodColor } = getTimeSeriesColor(ChartType.LOG_RATE); return ( { - if (isLogsSignal(dataStreamTypes) && !hasLogMetrics) { - return ; - } - const { currentPeriodColor } = getTimeSeriesColor(ChartType.LOG_ERROR_RATE); return ( diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/charts/helper/get_metrics_formulas.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/charts/helper/get_metrics_formulas.tsx index cba76728a0222..f2d014aa0a1dc 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/charts/helper/get_metrics_formulas.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/charts/helper/get_metrics_formulas.tsx @@ -11,7 +11,7 @@ export enum ChartMetricType { } const metricsFormulasMap: Record = { - [ChartMetricType.LOG_RATE]: `count(kql='log.level: *') / [PERIOD_IN_MINUTES]`, + [ChartMetricType.LOG_RATE]: `count() / [PERIOD_IN_MINUTES]`, [ChartMetricType.LOG_ERROR_RATE]: `count(kql='log.level: "error" OR log.level: "ERROR" OR error.log.level: "error" OR error.log.level: "ERROR"') / [PERIOD_IN_MINUTES]`, }; diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_log_metrics.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_log_metrics.tsx deleted file mode 100644 index 480795c533944..0000000000000 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_log_metrics.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 { EuiLink } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import React from 'react'; -import { PopoverBadge } from '../popover_badge'; - -export function NotAvailableLogsMetrics() { - return ( - - {i18n.translate( - 'xpack.apm.servicesTable.notAvailableLogsMetrics.content.logLevelLink', - { defaultMessage: 'log.level' } - )} - - ), - }} - /> - } - footer={ - - {i18n.translate('xpack.apm.servicesTable.notAvailableLogsMetrics.footer.learnMore', { - defaultMessage: 'Learn more', - })} - - } - /> - ); -} diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/built_in/services.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/built_in/services.ts index e52abf4142fb4..1c04fe8a924ac 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/built_in/services.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/built_in/services.ts @@ -20,7 +20,7 @@ const serviceTransactionFilter = (additionalFilters: string[] = []) => { export const builtInServicesFromLogsEntityDefinition: EntityDefinition = entityDefinitionSchema.parse({ - version: '1.0.2', + version: '1.0.3', id: `${BUILT_IN_ID_PREFIX}services_from_ecs_data`, name: 'Services from ECS data', description: @@ -121,7 +121,7 @@ export const builtInServicesFromLogsEntityDefinition: EntityDefinition = { name: 'A', aggregation: 'doc_count', - filter: 'log.level: * OR error.log.level: *', + filter: 'data_stream.type: logs', }, ], }, diff --git a/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_error_rate_timeseries/get_logs_error_rate_timeseries.ts b/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_error_rate_timeseries/get_logs_error_rate_timeseries.ts index d56f237035e0c..4766436fdebfc 100644 --- a/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_error_rate_timeseries/get_logs_error_rate_timeseries.ts +++ b/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_error_rate_timeseries/get_logs_error_rate_timeseries.ts @@ -10,7 +10,7 @@ import { ElasticsearchClient } from '@kbn/core/server'; import { estypes } from '@elastic/elasticsearch'; import { getBucketSizeFromTimeRangeAndBucketCount } from '../../utils'; import { ERROR_LOG_LEVEL, LOG_LEVEL } from '../../es_fields'; -import { existsQuery, kqlQuery } from '../../utils/es_queries'; +import { kqlQuery } from '../../utils/es_queries'; export interface LogsErrorRateTimeseries { esClient: ElasticsearchClient; @@ -81,7 +81,6 @@ export function createGetLogErrorRateTimeseries() { query: { bool: { filter: [ - ...existsQuery(LOG_LEVEL), ...kqlQuery(kuery), { terms: { diff --git a/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_rate_timeseries/get_logs_rate_timeseries.ts b/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_rate_timeseries/get_logs_rate_timeseries.ts index 3341170832a39..cdcf360405e75 100644 --- a/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_rate_timeseries/get_logs_rate_timeseries.ts +++ b/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_rate_timeseries/get_logs_rate_timeseries.ts @@ -9,8 +9,7 @@ import type { AggregationOptionsByType, AggregationResultOf } from '@kbn/es-type import { ElasticsearchClient } from '@kbn/core/server'; import { estypes } from '@elastic/elasticsearch'; import { getBucketSizeFromTimeRangeAndBucketCount } from '../../utils'; -import { LOG_LEVEL } from '../../es_fields'; -import { existsQuery, kqlQuery } from '../../utils/es_queries'; +import { kqlQuery } from '../../utils/es_queries'; export interface LogsRateTimeseries { esClient: ElasticsearchClient; @@ -59,7 +58,6 @@ export function createGetLogsRateTimeseries() { query: { bool: { filter: [ - ...existsQuery(LOG_LEVEL), ...kqlQuery(kuery), { terms: { From e528c8bd8e49ec928df8f08a219d7b4df79c81cb Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Fri, 13 Sep 2024 14:48:51 +0200 Subject: [PATCH 13/17] [ML] Functional tests - stabilize forecast tests (#192737) ## Summary This PR stabilizes and re-enables the forecast tests in the ML permission and results view test suites by waiting for global loading to finish before checking the forecast `Run` button state. ### Details I was able to reproduce the original failures by running the test suites in my local browser with network speed throttled to `3G`: the forecast modal opened and the `Run` button was disabled during global page loading, so the enabled check failed. Introducing the global loading wait fixed the tests for the throttled local runs. Closes #189565 Closes #189537 Closes #164381 --- .../apps/ml/anomaly_detection_result_views/forecasts.ts | 3 +-- x-pack/test/functional/apps/ml/permissions/full_ml_access.ts | 4 +--- x-pack/test/functional/services/ml/forecast.ts | 4 +++- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/forecasts.ts b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/forecasts.ts index 89eb1b9361272..3a60e8fca97c2 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/forecasts.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/forecasts.ts @@ -42,8 +42,7 @@ export default function ({ getService }: FtrProviderContext) { describe('forecasts', function () { this.tags(['ml']); - // FLAKY: https://github.com/elastic/kibana/issues/164381 - describe.skip('with single metric job', function () { + describe('with single metric job', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); diff --git a/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts b/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts index 3b8b33f853f6a..b986d29c7fee7 100644 --- a/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts +++ b/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts @@ -176,9 +176,7 @@ export default function ({ getService }: FtrProviderContext) { }); for (const testUser of testUsers) { - // FLAKY: https://github.com/elastic/kibana/issues/189565 - // FLAKY: https://github.com/elastic/kibana/issues/189537 - describe.skip(`(${testUser.user})`, function () { + describe(`(${testUser.user})`, function () { before(async () => { await ml.securityUI.loginAs(testUser.user); }); diff --git a/x-pack/test/functional/services/ml/forecast.ts b/x-pack/test/functional/services/ml/forecast.ts index 8b131578fd65f..ab0664b0f077f 100644 --- a/x-pack/test/functional/services/ml/forecast.ts +++ b/x-pack/test/functional/services/ml/forecast.ts @@ -9,9 +9,10 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -export function MachineLearningForecastProvider({ getService }: FtrProviderContext) { +export function MachineLearningForecastProvider({ getPageObject, getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const retry = getService('retry'); + const headerPage = getPageObject('header'); return { async assertForecastButtonExists() { @@ -102,6 +103,7 @@ export function MachineLearningForecastProvider({ getService }: FtrProviderConte }, async assertForecastModalRunButtonEnabled(expectedValue: boolean) { + await headerPage.waitUntilLoadingHasFinished(); const isEnabled = await testSubjects.isEnabled('mlModalForecast > mlModalForecastButtonRun'); expect(isEnabled).to.eql( expectedValue, From 3cc99fc627a30b980d844f9faddb1b346d942499 Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Fri, 13 Sep 2024 14:53:45 +0200 Subject: [PATCH 14/17] Revert "[Fleet] use @kbn/config-schema in Fleet API (Part 1) (#192447)" (#192840) This reverts commit 1116ac6daaef45785dad05ad09f78a93768e591d. Related to https://github.com/elastic/kibana/issues/184685 There were a few issues reported by the security team (like https://github.com/elastic/kibana/pull/192832) It seems we have gaps in the test coverage, and adding the response schemas to the code is impacting the API as it adds validation on the response objects. I'll reopen the pr with adding more test coverage to prevent other errors. --- .../server/routes/package_policy/index.ts | 278 +----------------- .../fleet/server/routes/schema/errors.ts | 33 --- .../fleet/server/routes/settings/index.ts | 165 +---------- .../fleet/server/routes/setup/index.ts | 100 +------ .../routes/standalone_agent_api_key/index.ts | 19 +- .../server/routes/uninstall_token/index.ts | 60 +--- .../server/types/models/package_policy.ts | 257 +++++----------- .../fleet/server/types/rest_spec/common.ts | 5 +- .../server/types/rest_spec/package_policy.ts | 12 +- .../server/types/rest_spec/uninstall_token.ts | 15 +- 10 files changed, 95 insertions(+), 849 deletions(-) delete mode 100644 x-pack/plugins/fleet/server/routes/schema/errors.ts diff --git a/x-pack/plugins/fleet/server/routes/package_policy/index.ts b/x-pack/plugins/fleet/server/routes/package_policy/index.ts index d28709eebaac3..893eb37a9b1bc 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/index.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/index.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { schema } from '@kbn/config-schema'; import { getRouteRequiredAuthz } from '../../services/security'; @@ -23,15 +22,9 @@ import { DryRunPackagePoliciesRequestSchema, DeleteOnePackagePolicyRequestSchema, BulkGetPackagePoliciesRequestSchema, - PackagePolicyPackageSchema, - PackagePolicyResponseSchema, - PackagePolicyStatusResponseSchema, - DryRunPackagePolicySchema, } from '../../types'; import { calculateRouteAuthz } from '../../services/security/security'; -import { genericErrorResponse, notFoundResponse } from '../schema/errors'; - import { getPackagePoliciesHandler, getOnePackagePolicyHandler, @@ -55,31 +48,11 @@ export const registerRoutes = (router: FleetAuthzRouter) => { fleetAuthz, getRouteRequiredAuthz('get', PACKAGE_POLICY_API_ROUTES.LIST_PATTERN) ).granted, - description: 'List package policies', - options: { - tags: ['oas-tag:Fleet package policies'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: GetPackagePoliciesRequestSchema, - response: { - 200: { - body: () => - schema.object({ - items: schema.arrayOf(PackagePolicyResponseSchema), - total: schema.number(), - page: schema.number(), - perPage: schema.number(), - }), - }, - 400: { - body: genericErrorResponse, - }, - }, - }, + validate: { request: GetPackagePoliciesRequestSchema }, }, getPackagePoliciesHandler ); @@ -93,31 +66,11 @@ export const registerRoutes = (router: FleetAuthzRouter) => { fleetAuthz, getRouteRequiredAuthz('post', PACKAGE_POLICY_API_ROUTES.BULK_GET_PATTERN) ).granted, - description: 'Bulk get package policies', - options: { - tags: ['oas-tag:Fleet package policies'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: BulkGetPackagePoliciesRequestSchema, - response: { - 200: { - body: () => - schema.object({ - items: schema.arrayOf(PackagePolicyResponseSchema), - }), - }, - 400: { - body: genericErrorResponse, - }, - 404: { - body: notFoundResponse, - }, - }, - }, + validate: { request: BulkGetPackagePoliciesRequestSchema }, }, bulkGetPackagePoliciesHandler ); @@ -131,31 +84,11 @@ export const registerRoutes = (router: FleetAuthzRouter) => { fleetAuthz, getRouteRequiredAuthz('get', PACKAGE_POLICY_API_ROUTES.INFO_PATTERN) ).granted, - description: 'Get package policy by ID', - options: { - tags: ['oas-tag:Fleet package policies'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: GetOnePackagePolicyRequestSchema, - response: { - 200: { - body: () => - schema.object({ - item: PackagePolicyResponseSchema, - }), - }, - 400: { - body: genericErrorResponse, - }, - 404: { - body: notFoundResponse, - }, - }, - }, + validate: { request: GetOnePackagePolicyRequestSchema }, }, getOnePackagePolicyHandler ); @@ -176,35 +109,14 @@ export const registerRoutes = (router: FleetAuthzRouter) => { ); // Create - // Authz check moved to service here: https://github.com/elastic/kibana/pull/140458 router.versioned .post({ path: PACKAGE_POLICY_API_ROUTES.CREATE_PATTERN, - description: 'Create package policy', - options: { - tags: ['oas-tag:Fleet package policies'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: CreatePackagePolicyRequestSchema, - response: { - 200: { - body: () => - schema.object({ - item: PackagePolicyResponseSchema, - }), - }, - 400: { - body: genericErrorResponse, - }, - 409: { - body: genericErrorResponse, - }, - }, - }, + validate: { request: CreatePackagePolicyRequestSchema }, }, createPackagePolicyHandler ); @@ -218,32 +130,11 @@ export const registerRoutes = (router: FleetAuthzRouter) => { fleetAuthz, getRouteRequiredAuthz('put', PACKAGE_POLICY_API_ROUTES.UPDATE_PATTERN) ).granted, - description: 'Update package policy by ID', - options: { - tags: ['oas-tag:Fleet package policies'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: UpdatePackagePolicyRequestSchema, - response: { - 200: { - body: () => - schema.object({ - item: PackagePolicyResponseSchema, - success: schema.boolean(), - }), - }, - 400: { - body: genericErrorResponse, - }, - 403: { - body: genericErrorResponse, - }, - }, - }, + validate: { request: UpdatePackagePolicyRequestSchema }, }, updatePackagePolicyHandler @@ -256,42 +147,11 @@ export const registerRoutes = (router: FleetAuthzRouter) => { fleetAuthz: { integrations: { writeIntegrationPolicies: true }, }, - description: 'Bulk delete package policies', - options: { - tags: ['oas-tag:Fleet package policies'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: DeletePackagePoliciesRequestSchema, - response: { - 200: { - body: () => - schema.arrayOf( - PackagePolicyStatusResponseSchema.extends({ - policy_id: schema.nullable( - schema.maybe( - schema.string({ - meta: { - description: 'Use `policy_ids` instead', - deprecated: true, - }, - }) - ) - ), - policy_ids: schema.arrayOf(schema.string()), - output_id: schema.nullable(schema.maybe(schema.string())), - package: PackagePolicyPackageSchema, - }) - ), - }, - 400: { - body: genericErrorResponse, - }, - }, - }, + validate: { request: DeletePackagePoliciesRequestSchema }, }, deletePackagePolicyHandler ); @@ -302,28 +162,11 @@ export const registerRoutes = (router: FleetAuthzRouter) => { fleetAuthz: { integrations: { writeIntegrationPolicies: true }, }, - description: 'Delete package policy by ID', - options: { - tags: ['oas-tag:Fleet package policies'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: DeleteOnePackagePolicyRequestSchema, - response: { - 200: { - body: () => - schema.object({ - id: schema.string(), - }), - }, - 400: { - body: genericErrorResponse, - }, - }, - }, + validate: { request: DeleteOnePackagePolicyRequestSchema }, }, deleteOnePackagePolicyHandler ); @@ -335,25 +178,11 @@ export const registerRoutes = (router: FleetAuthzRouter) => { fleetAuthz: { integrations: { writeIntegrationPolicies: true }, }, - description: 'Upgrade package policy to a newer package version', - options: { - tags: ['oas-tag:Fleet package policies'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: UpgradePackagePoliciesRequestSchema, - response: { - 200: { - body: () => PackagePolicyStatusResponseSchema, - }, - 400: { - body: genericErrorResponse, - }, - }, - }, + validate: { request: UpgradePackagePoliciesRequestSchema }, }, upgradePackagePolicyHandler ); @@ -365,100 +194,11 @@ export const registerRoutes = (router: FleetAuthzRouter) => { fleetAuthz: { integrations: { readIntegrationPolicies: true }, }, - description: 'Dry run package policy upgrade', - options: { - tags: ['oas-tag:Fleet package policies'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: DryRunPackagePoliciesRequestSchema, - response: { - 200: { - body: () => - schema.arrayOf( - schema.object({ - name: schema.maybe(schema.string()), - statusCode: schema.maybe(schema.number()), - body: schema.maybe(schema.object({ message: schema.string() })), - hasErrors: schema.boolean(), - diff: schema.maybe( - schema.arrayOf( - schema.oneOf([PackagePolicyResponseSchema, DryRunPackagePolicySchema]) - ) - ), - agent_diff: schema.maybe( - schema.arrayOf( - schema.arrayOf( - schema - .object({ - id: schema.string(), - name: schema.string(), - revision: schema.number(), - type: schema.string(), - data_stream: schema.object({ - namespace: schema.string(), - }), - use_output: schema.string(), - package_policy_id: schema.string(), - meta: schema.maybe( - schema.object({ - package: schema - .object({ - name: schema.string(), - version: schema.string(), - }) - .extendsDeep({ - // equivalent of allowing extra keys like `[key: string]: any;` - unknowns: 'allow', - }), - }) - ), - streams: schema.maybe( - schema.arrayOf( - schema - .object({ - id: schema.string(), - data_stream: schema.object({ - dataset: schema.string(), - type: schema.string(), - }), - }) - .extendsDeep({ - unknowns: 'allow', - }) - ) - ), - processors: schema.maybe( - schema.arrayOf( - schema.object({ - add_fields: schema.object({ - target: schema.string(), - fields: schema.recordOf( - schema.string(), - schema.oneOf([schema.string(), schema.number()]) - ), - }), - }) - ) - ), - }) - .extendsDeep({ - unknowns: 'allow', - }) - ) - ) - ), - }) - ), - }, - 400: { - body: genericErrorResponse, - }, - }, - }, + validate: { request: DryRunPackagePoliciesRequestSchema }, }, dryRunUpgradePackagePolicyHandler ); diff --git a/x-pack/plugins/fleet/server/routes/schema/errors.ts b/x-pack/plugins/fleet/server/routes/schema/errors.ts deleted file mode 100644 index 1d8f0f5d5b92d..0000000000000 --- a/x-pack/plugins/fleet/server/routes/schema/errors.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 { schema } from '@kbn/config-schema'; - -export const genericErrorResponse = () => - schema.object( - { - statusCode: schema.number(), - error: schema.string(), - message: schema.string(), - }, - { - meta: { description: 'Generic Error' }, - } - ); - -export const notFoundResponse = () => - schema.object({ - message: schema.string(), - }); - -export const internalErrorResponse = () => - schema.object( - { - message: schema.string(), - }, - { meta: { description: 'Internal Server Error' } } - ); diff --git a/x-pack/plugins/fleet/server/routes/settings/index.ts b/x-pack/plugins/fleet/server/routes/settings/index.ts index 31bbf61db2a51..b9f672627daa7 100644 --- a/x-pack/plugins/fleet/server/routes/settings/index.ts +++ b/x-pack/plugins/fleet/server/routes/settings/index.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { schema } from '@kbn/config-schema'; import { parseExperimentalConfigValue } from '../../../common/experimental_features'; import { API_VERSIONS } from '../../../common/constants'; @@ -19,8 +18,6 @@ import { } from '../../types'; import type { FleetConfigType } from '../../config'; -import { genericErrorResponse, notFoundResponse } from '../schema/errors'; - import { getEnrollmentSettingsHandler } from './enrollment_settings_handler'; import { @@ -30,36 +27,6 @@ import { putSpaceSettingsHandler, } from './settings_handler'; -const spaceSettingsResponse = () => - schema.object({ - item: schema.object({ - managed_by: schema.maybe(schema.string()), - allowed_namespace_prefixes: schema.arrayOf(schema.string()), - }), - }); - -const settingsResponse = () => - schema.object({ - item: schema.object({ - has_seen_add_data_notice: schema.maybe(schema.boolean()), - fleet_server_hosts: schema.maybe(schema.arrayOf(schema.string())), - prerelease_integrations_enabled: schema.boolean(), - id: schema.string(), - version: schema.maybe(schema.string()), - preconfigured_fields: schema.maybe(schema.arrayOf(schema.literal('fleet_server_hosts'))), - secret_storage_requirements_met: schema.maybe(schema.boolean()), - output_secret_storage_requirements_met: schema.maybe(schema.boolean()), - use_space_awareness_migration_status: schema.maybe( - schema.oneOf([ - schema.literal('pending'), - schema.literal('success'), - schema.literal('error'), - ]) - ), - use_space_awareness_migration_started_at: schema.maybe(schema.string()), - }), - }); - export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType) => { const experimentalFeatures = parseExperimentalConfigValue(config.enableExperimental); if (experimentalFeatures.useSpaceAwareness) { @@ -78,14 +45,7 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: GetSpaceSettingsRequestSchema, - response: { - 200: { - body: spaceSettingsResponse, - }, - }, - }, + validate: { request: GetSpaceSettingsRequestSchema }, }, getSpaceSettingsHandler ); @@ -101,14 +61,7 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: PutSpaceSettingsRequestSchema, - response: { - 200: { - body: spaceSettingsResponse, - }, - }, - }, + validate: { request: PutSpaceSettingsRequestSchema }, }, putSpaceSettingsHandler ); @@ -121,27 +74,11 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType fleet: { readSettings: true }, }, description: `Get settings`, - options: { - tags: ['oas-tag:Fleet internals'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: GetSettingsRequestSchema, - response: { - 200: { - body: settingsResponse, - }, - 400: { - body: genericErrorResponse, - }, - 404: { - body: notFoundResponse, - }, - }, - }, + validate: { request: GetSettingsRequestSchema }, }, getSettingsHandler ); @@ -152,27 +89,11 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType fleet: { allSettings: true }, }, description: `Update settings`, - options: { - tags: ['oas-tag:Fleet internals'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: PutSettingsRequestSchema, - response: { - 200: { - body: settingsResponse, - }, - 400: { - body: genericErrorResponse, - }, - 404: { - body: notFoundResponse, - }, - }, - }, + validate: { request: PutSettingsRequestSchema }, }, putSettingsHandler ); @@ -183,87 +104,11 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType return authz.fleet.addAgents || authz.fleet.addFleetServers; }, description: `Get enrollment settings`, - options: { - tags: ['oas-tag:Fleet internals'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: GetEnrollmentSettingsRequestSchema, - response: { - 200: { - body: () => - schema.object({ - fleet_server: schema.object({ - policies: schema.arrayOf( - schema.object({ - id: schema.string(), - name: schema.string(), - is_managed: schema.boolean(), - is_default_fleet_server: schema.maybe(schema.boolean()), - has_fleet_server: schema.maybe(schema.boolean()), - fleet_server_host_id: schema.nullable(schema.maybe(schema.string())), - download_source_id: schema.nullable(schema.maybe(schema.string())), - space_ids: schema.maybe(schema.arrayOf(schema.string())), - }) - ), - has_active: schema.boolean(), - host: schema.maybe( - schema.object({ - id: schema.string(), - name: schema.string(), - host_urls: schema.arrayOf(schema.string()), - is_default: schema.boolean(), - is_preconfigured: schema.boolean(), - is_internal: schema.maybe(schema.boolean()), - proxy_id: schema.nullable(schema.maybe(schema.string())), - }) - ), - host_proxy: schema.maybe( - schema.object({ - id: schema.string(), - proxy_headers: schema.maybe( - schema.recordOf( - schema.string(), - schema.oneOf([schema.string(), schema.number(), schema.boolean()]) - ) - ), - name: schema.string(), - url: schema.string(), - certificate_authorities: schema.nullable(schema.maybe(schema.string())), - certificate: schema.nullable(schema.maybe(schema.string())), - certificate_key: schema.nullable(schema.maybe(schema.string())), - is_preconfigured: schema.boolean(), - }) - ), - }), - download_source: schema.maybe( - schema.object({ - id: schema.string(), - name: schema.string(), - host: schema.string(), - is_default: schema.boolean(), - proxy_id: schema.nullable( - schema.maybe( - schema.string({ - meta: { - description: - 'The ID of the proxy to use for this download source. See the proxies API for more information.', - }, - }) - ) - ), - }) - ), - }), - }, - 400: { - body: genericErrorResponse, - }, - }, - }, + validate: { request: GetEnrollmentSettingsRequestSchema }, }, getEnrollmentSettingsHandler ); diff --git a/x-pack/plugins/fleet/server/routes/setup/index.ts b/x-pack/plugins/fleet/server/routes/setup/index.ts index e465ca4d9ec1d..7052aacfc329d 100644 --- a/x-pack/plugins/fleet/server/routes/setup/index.ts +++ b/x-pack/plugins/fleet/server/routes/setup/index.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { schema } from '@kbn/config-schema'; import type { FleetAuthzRouter } from '../../services/security'; @@ -13,29 +12,8 @@ import { API_VERSIONS } from '../../../common/constants'; import type { FleetConfigType } from '../../../common/types'; -import { genericErrorResponse, internalErrorResponse } from '../schema/errors'; - import { getFleetStatusHandler, fleetSetupHandler } from './handlers'; -const fleetSetupResponseBody = () => - schema.object( - { - isInitialized: schema.boolean(), - nonFatalErrors: schema.arrayOf( - schema.object({ - name: schema.string(), - message: schema.string(), - }) - ), - }, - { - meta: { - description: - "A summary of the result of Fleet's `setup` lifecycle. If `isInitialized` is true, Fleet is ready to accept agent enrollment. `nonFatalErrors` may include useful insight into non-blocking issues with Fleet setup.", - }, - } - ); - export const registerFleetSetupRoute = (router: FleetAuthzRouter) => { router.versioned .post({ @@ -44,27 +22,11 @@ export const registerFleetSetupRoute = (router: FleetAuthzRouter) => { fleet: { setup: true }, }, description: `Initiate Fleet setup`, - options: { - tags: ['oas-tag:Fleet internals'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: {}, - response: { - 200: { - body: fleetSetupResponseBody, - }, - 400: { - body: genericErrorResponse, - }, - 500: { - body: internalErrorResponse, - }, - }, - }, + validate: false, }, fleetSetupHandler ); @@ -78,25 +40,11 @@ export const registerCreateFleetSetupRoute = (router: FleetAuthzRouter) => { fleetAuthz: { fleet: { setup: true }, }, - description: `Initiate agent setup`, - options: { - tags: ['oas-tag:Elastic Agents'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: {}, - response: { - 200: { - body: fleetSetupResponseBody, - }, - 400: { - body: genericErrorResponse, - }, - }, - }, + validate: false, }, fleetSetupHandler ); @@ -109,53 +57,11 @@ export const registerGetFleetStatusRoute = (router: FleetAuthzRouter) => { fleetAuthz: { fleet: { setup: true }, }, - description: `Get agent setup info`, - options: { - tags: ['oas-tag:Elastic Agents'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: {}, - response: { - 200: { - body: () => - schema.object( - { - isReady: schema.boolean(), - missing_requirements: schema.arrayOf( - schema.oneOf([ - schema.literal('security_required'), - schema.literal('tls_required'), - schema.literal('api_keys'), - schema.literal('fleet_admin_user'), - schema.literal('fleet_server'), - ]) - ), - missing_optional_features: schema.arrayOf( - schema.oneOf([ - schema.literal('encrypted_saved_object_encryption_key_required'), - ]) - ), - package_verification_key_id: schema.maybe(schema.string()), - is_space_awareness_enabled: schema.maybe(schema.boolean()), - is_secrets_storage_enabled: schema.maybe(schema.boolean()), - }, - { - meta: { - description: - 'A summary of the agent setup status. `isReady` indicates whether the setup is ready. If the setup is not ready, `missing_requirements` lists which requirements are missing.', - }, - } - ), - }, - 400: { - body: genericErrorResponse, - }, - }, - }, + validate: false, }, getFleetStatusHandler ); diff --git a/x-pack/plugins/fleet/server/routes/standalone_agent_api_key/index.ts b/x-pack/plugins/fleet/server/routes/standalone_agent_api_key/index.ts index 15e823d02cd2a..9255f058aee46 100644 --- a/x-pack/plugins/fleet/server/routes/standalone_agent_api_key/index.ts +++ b/x-pack/plugins/fleet/server/routes/standalone_agent_api_key/index.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { schema } from '@kbn/config-schema'; import type { FleetAuthzRouter } from '../../services/security'; @@ -28,23 +27,7 @@ export const registerRoutes = (router: FleetAuthzRouter) => { .addVersion( { version: API_VERSIONS.internal.v1, - validate: { - request: PostStandaloneAgentAPIKeyRequestSchema, - response: { - 200: { - body: () => - schema.object({ - item: schema.object({ - id: schema.string(), - api_key: schema.string(), - name: schema.string(), - encoded: schema.string(), - expiration: schema.maybe(schema.number()), - }), - }), - }, - }, - }, + validate: { request: PostStandaloneAgentAPIKeyRequestSchema }, }, createStandaloneAgentApiKeyHandler ); diff --git a/x-pack/plugins/fleet/server/routes/uninstall_token/index.ts b/x-pack/plugins/fleet/server/routes/uninstall_token/index.ts index 21b69adb0d4b2..9fb91b45fa373 100644 --- a/x-pack/plugins/fleet/server/routes/uninstall_token/index.ts +++ b/x-pack/plugins/fleet/server/routes/uninstall_token/index.ts @@ -4,8 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { schema } from '@kbn/config-schema'; - import { UNINSTALL_TOKEN_ROUTES, API_VERSIONS } from '../../../common/constants'; import type { FleetConfigType } from '../../config'; @@ -16,8 +14,6 @@ import { } from '../../types/rest_spec/uninstall_token'; import { parseExperimentalConfigValue } from '../../../common/experimental_features'; -import { genericErrorResponse } from '../schema/errors'; - import { getUninstallTokenHandler, getUninstallTokensMetadataHandler } from './handlers'; export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType) => { @@ -30,39 +26,11 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType fleetAuthz: { fleet: { allAgents: true }, }, - description: 'List metadata for latest uninstall tokens per agent policy', - options: { - tags: ['oas-tag:Fleet uninstall tokens'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: GetUninstallTokensMetadataRequestSchema, - response: { - 200: { - body: () => - schema.object({ - items: schema.arrayOf( - schema.object({ - id: schema.string(), - policy_id: schema.string(), - policy_name: schema.maybe(schema.string()), - created_at: schema.string(), - namespaces: schema.maybe(schema.arrayOf(schema.string())), - }) - ), - total: schema.number(), - page: schema.number(), - perPage: schema.number(), - }), - }, - 400: { - body: genericErrorResponse, - }, - }, - }, + validate: { request: GetUninstallTokensMetadataRequestSchema }, }, getUninstallTokensMetadataHandler ); @@ -73,35 +41,11 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType fleetAuthz: { fleet: { allAgents: true }, }, - description: 'Get one decrypted uninstall token by its ID', - options: { - tags: ['oas-tag:Fleet uninstall tokens'], - }, }) .addVersion( { version: API_VERSIONS.public.v1, - validate: { - request: GetUninstallTokenRequestSchema, - response: { - 200: { - body: () => - schema.object({ - item: schema.object({ - id: schema.string(), - token: schema.string(), - policy_id: schema.string(), - policy_name: schema.maybe(schema.string()), - created_at: schema.string(), - namespaces: schema.maybe(schema.arrayOf(schema.string())), - }), - }), - }, - 400: { - body: genericErrorResponse, - }, - }, - }, + validate: { request: GetUninstallTokenRequestSchema }, }, getUninstallTokenHandler ); diff --git a/x-pack/plugins/fleet/server/types/models/package_policy.ts b/x-pack/plugins/fleet/server/types/models/package_policy.ts index 3b41b80efe42c..e105bc82b27db 100644 --- a/x-pack/plugins/fleet/server/types/models/package_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/package_policy.ts @@ -16,24 +16,15 @@ export const PackagePolicyNamespaceSchema = schema.string({ return namespaceValidation.error; } }, - meta: { - description: - "The package policy namespace. Leave blank to inherit the agent policy's namespace.", - }, }); -export const ConfigRecordSchema = schema.recordOf( +const ConfigRecordSchema = schema.recordOf( schema.string(), schema.object({ type: schema.maybe(schema.string()), value: schema.maybe(schema.any()), frozen: schema.maybe(schema.boolean()), - }), - { - meta: { - description: 'Package variable (see integration documentation for more information)', - }, - } + }) ); const PackagePolicyStreamsSchema = { @@ -71,7 +62,7 @@ const PackagePolicyStreamsSchema = { compiled_stream: schema.maybe(schema.any()), }; -export const PackagePolicyInputsSchema = { +const PackagePolicyInputsSchema = { type: schema.string(), policy_template: schema.maybe(schema.string()), enabled: schema.boolean(), @@ -101,88 +92,43 @@ const ExperimentalDataStreamFeatures = schema.arrayOf( }) ); -export const PackagePolicyPackageSchema = schema.object({ - name: schema.string({ - meta: { - description: 'Package name', - }, - }), - title: schema.maybe(schema.string()), - version: schema.string({ - meta: { - description: 'Package version', - }, - }), - experimental_data_stream_features: schema.maybe(ExperimentalDataStreamFeatures), - requires_root: schema.maybe(schema.boolean()), -}); - -export const PackagePolicyBaseSchema = { - name: schema.string({ - meta: { - description: 'Package policy name (should be unique)', - }, - }), - description: schema.maybe( - schema.string({ - meta: { - description: 'Package policy description', - }, - }) - ), +const PackagePolicyBaseSchema = { + name: schema.string(), + description: schema.maybe(schema.string()), namespace: schema.maybe(PackagePolicyNamespaceSchema), - policy_id: schema.nullable( - schema.maybe( - schema.string({ - meta: { - description: 'Agent policy ID where that package policy will be added', - deprecated: true, - }, - }) - ) - ), - policy_ids: schema.maybe( - schema.arrayOf( - schema.string({ - meta: { - description: 'Agent policy IDs where that package policy will be added', - }, - }) - ) - ), + policy_id: schema.nullable(schema.maybe(schema.string())), + policy_ids: schema.maybe(schema.arrayOf(schema.string())), output_id: schema.nullable(schema.maybe(schema.string())), enabled: schema.boolean(), is_managed: schema.maybe(schema.boolean()), - package: schema.maybe(PackagePolicyPackageSchema), - + package: schema.maybe( + schema.object({ + name: schema.string(), + title: schema.string(), + version: schema.string(), + experimental_data_stream_features: schema.maybe(ExperimentalDataStreamFeatures), + requires_root: schema.maybe(schema.boolean()), + }) + ), inputs: schema.arrayOf(schema.object(PackagePolicyInputsSchema)), vars: schema.maybe(ConfigRecordSchema), overrides: schema.maybe( schema.nullable( - schema.object( - { - inputs: schema.maybe( - schema.recordOf(schema.string(), schema.any(), { - validate: (val) => { - if ( - Object.keys(val).some( - (key) => - key.match(/^compiled_inputs(\.)?/) || key.match(/^compiled_stream(\.)?/) - ) - ) { - return 'Overrides of compiled_inputs and compiled_stream are not allowed'; - } - }, - }) - ), - }, - { - meta: { - description: - 'Override settings that are defined in the package policy. The override option should be used only in unusual circumstances and not as a routine procedure.', - }, - } - ) + schema.object({ + inputs: schema.maybe( + schema.recordOf(schema.string(), schema.any(), { + validate: (val) => { + if ( + Object.keys(val).some( + (key) => key.match(/^compiled_inputs(\.)?/) || key.match(/^compiled_stream(\.)?/) + ) + ) { + return 'Overrides of compiled_inputs and compiled_stream are not allowed'; + } + }, + }) + ), + }) ) ), }; @@ -196,7 +142,15 @@ export const NewPackagePolicySchema = schema.object({ const CreatePackagePolicyProps = { ...PackagePolicyBaseSchema, enabled: schema.maybe(schema.boolean()), - package: schema.maybe(PackagePolicyPackageSchema), + package: schema.maybe( + schema.object({ + name: schema.string(), + title: schema.maybe(schema.string()), + version: schema.string(), + experimental_data_stream_features: schema.maybe(ExperimentalDataStreamFeatures), + requires_root: schema.maybe(schema.boolean()), + }) + ), inputs: schema.arrayOf( schema.object({ ...PackagePolicyInputsSchema, @@ -207,24 +161,11 @@ const CreatePackagePolicyProps = { export const CreatePackagePolicyRequestBodySchema = schema.object({ ...CreatePackagePolicyProps, - id: schema.maybe( - schema.string({ - meta: { - description: 'Package policy unique identifier', - }, - }) - ), - force: schema.maybe( - schema.boolean({ - meta: { - description: - 'Force package policy creation even if package is not verified, or if the agent policy is managed.', - }, - }) - ), + id: schema.maybe(schema.string()), + force: schema.maybe(schema.boolean()), }); -export const SimplifiedVarsSchema = schema.recordOf( +const SimplifiedVarsSchema = schema.recordOf( schema.string(), schema.nullable( schema.oneOf([ @@ -239,55 +180,6 @@ export const SimplifiedVarsSchema = schema.recordOf( isSecretRef: schema.boolean(), }), ]) - ), - { - meta: { - description: - 'Input/stream level variable (see integration documentation for more information)', - }, - } -); - -export const SimplifiedPackagePolicyInputsSchema = schema.maybe( - schema.recordOf( - schema.string(), - schema.object({ - enabled: schema.maybe( - schema.boolean({ - meta: { - description: 'enable or disable that input, (default to true)', - }, - }) - ), - vars: schema.maybe(SimplifiedVarsSchema), - streams: schema.maybe( - schema.recordOf( - schema.string(), - schema.object({ - enabled: schema.maybe( - schema.boolean({ - meta: { - description: 'enable or disable that stream, (default to true)', - }, - }) - ), - vars: schema.maybe(SimplifiedVarsSchema), - }), - { - meta: { - description: - 'Input streams (see integration documentation to know what streams are available)', - }, - } - ) - ), - }), - { - meta: { - description: - 'Package policy inputs (see integration documentation to know what inputs are available)', - }, - } ) ); @@ -298,7 +190,24 @@ export const SimplifiedPackagePolicyBaseSchema = schema.object({ namespace: schema.maybe(schema.string()), output_id: schema.nullable(schema.maybe(schema.string())), vars: schema.maybe(SimplifiedVarsSchema), - inputs: SimplifiedPackagePolicyInputsSchema, + inputs: schema.maybe( + schema.recordOf( + schema.string(), + schema.object({ + enabled: schema.maybe(schema.boolean()), + vars: schema.maybe(SimplifiedVarsSchema), + streams: schema.maybe( + schema.recordOf( + schema.string(), + schema.object({ + enabled: schema.maybe(schema.boolean()), + vars: schema.maybe(SimplifiedVarsSchema), + }) + ) + ), + }) + ) + ), }); export const SimplifiedPackagePolicyPreconfiguredSchema = SimplifiedPackagePolicyBaseSchema.extends( @@ -315,7 +224,12 @@ export const SimplifiedCreatePackagePolicyRequestBodySchema = policy_id: schema.nullable(schema.maybe(schema.string())), policy_ids: schema.maybe(schema.arrayOf(schema.string())), force: schema.maybe(schema.boolean()), - package: PackagePolicyPackageSchema, + package: schema.object({ + name: schema.string(), + version: schema.string(), + experimental_data_stream_features: schema.maybe(ExperimentalDataStreamFeatures), + requires_root: schema.maybe(schema.boolean()), + }), }); export const UpdatePackagePolicyRequestBodySchema = schema.object({ @@ -370,38 +284,3 @@ export const PackagePolicySchema = schema.object({ ) ), }); - -export const PackagePolicyResponseSchema = PackagePolicySchema.extends({ - vars: schema.maybe(schema.oneOf([ConfigRecordSchema, schema.maybe(SimplifiedVarsSchema)])), - inputs: schema.oneOf([ - schema.arrayOf( - schema.object({ - ...PackagePolicyInputsSchema, - compiled_input: schema.maybe(schema.any()), - }) - ), - SimplifiedPackagePolicyInputsSchema, - ]), -}); - -export const DryRunPackagePolicySchema = schema.object({ - ...PackagePolicyBaseSchema, - id: schema.maybe(schema.string()), - force: schema.maybe(schema.boolean()), - errors: schema.maybe( - schema.arrayOf( - schema.object({ - message: schema.string(), - key: schema.maybe(schema.string()), - }) - ) - ), -}); - -export const PackagePolicyStatusResponseSchema = schema.object({ - id: schema.string(), - success: schema.boolean(), - name: schema.maybe(schema.string()), - statusCode: schema.maybe(schema.number()), - body: schema.maybe(schema.object({ message: schema.string() })), -}); diff --git a/x-pack/plugins/fleet/server/types/rest_spec/common.ts b/x-pack/plugins/fleet/server/types/rest_spec/common.ts index 2be083d677dd3..0c5f16ff87f90 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/common.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/common.ts @@ -23,10 +23,7 @@ export const ListWithKuerySchema = schema.object({ }); export const BulkRequestBodySchema = schema.object({ - ids: schema.arrayOf(schema.string(), { - minSize: 1, - meta: { description: 'list of package policy ids' }, - }), + ids: schema.arrayOf(schema.string(), { minSize: 1 }), ignoreMissing: schema.maybe(schema.boolean()), }); diff --git a/x-pack/plugins/fleet/server/types/rest_spec/package_policy.ts b/x-pack/plugins/fleet/server/types/rest_spec/package_policy.ts index 3b6fcbef34502..88b4452a5fe7a 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/package_policy.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/package_policy.ts @@ -71,14 +71,10 @@ export const GetOnePackagePolicyRequestSchema = { }; export const CreatePackagePolicyRequestSchema = { - body: schema.oneOf( - [CreatePackagePolicyRequestBodySchema, SimplifiedCreatePackagePolicyRequestBodySchema], - { - meta: { - description: 'You should use inputs as an object and not use the deprecated inputs array.', - }, - } - ), + body: schema.oneOf([ + CreatePackagePolicyRequestBodySchema, + SimplifiedCreatePackagePolicyRequestBodySchema, + ]), query: schema.object({ format: schema.maybe( schema.oneOf([schema.literal(inputsFormat.Simplified), schema.literal(inputsFormat.Legacy)]) diff --git a/x-pack/plugins/fleet/server/types/rest_spec/uninstall_token.ts b/x-pack/plugins/fleet/server/types/rest_spec/uninstall_token.ts index 010b3731776f8..924e1da2cb9e8 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/uninstall_token.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/uninstall_token.ts @@ -8,20 +8,9 @@ import { schema } from '@kbn/config-schema'; export const GetUninstallTokensMetadataRequestSchema = { query: schema.object({ - policyId: schema.maybe( - schema.string({ - maxLength: 50, - meta: { description: 'Partial match filtering for policy IDs' }, - }) - ), + policyId: schema.maybe(schema.string({ maxLength: 50 })), search: schema.maybe(schema.string({ maxLength: 50 })), - perPage: schema.maybe( - schema.number({ - defaultValue: 20, - min: 5, - meta: { description: 'The number of items to return' }, - }) - ), + perPage: schema.maybe(schema.number({ defaultValue: 20, min: 5 })), page: schema.maybe(schema.number({ defaultValue: 1, min: 1 })), }), }; From 8473cbd23fb9f3fd206d705e085248d3794ae9ce Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 13 Sep 2024 16:35:42 +0300 Subject: [PATCH 15/17] fix: [Index Management tables][SCREEN READER]: Checkboxes should have easy to understand names (#192466) Closes: https://github.com/elastic/search-team/issues/8217 ## Description Two table views in the Index Management group have checkboxes with ambiguous checkbox labels. ## What was changed?: 1. `selectableMessage` was updated: - `Select this component template` -> `Select "{name}" component template` - `Component template is in use and cannot be deleted` -> `Component template "{name}" is in use and cannot be deleted` ## Screen: image --- .../component_template_list/table.tsx | 12 +++++++++--- x-pack/plugins/translations/translations/fr-FR.json | 2 -- x-pack/plugins/translations/translations/ja-JP.json | 2 -- x-pack/plugins/translations/translations/zh-CN.json | 2 -- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_list/table.tsx b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_list/table.tsx index aa79ca41e6b25..ccc863d42c129 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/component_template_list/table.tsx +++ b/x-pack/plugins/index_management/public/application/components/component_templates/component_template_list/table.tsx @@ -146,13 +146,19 @@ export const ComponentTable: FunctionComponent = ({ selection: { onSelectionChange: setSelection, selectable: ({ usedBy }) => usedBy.length === 0, - selectableMessage: (selectable) => + selectableMessage: (selectable, { name }) => selectable ? i18n.translate('xpack.idxMgmt.componentTemplatesList.table.selectionLabel', { - defaultMessage: 'Select this component template', + defaultMessage: 'Select "{name}" component template', + values: { + name, + }, }) : i18n.translate('xpack.idxMgmt.componentTemplatesList.table.disabledSelectionLabel', { - defaultMessage: 'Component template is in use and cannot be deleted', + defaultMessage: 'Component template "{name}" is in use and cannot be deleted', + values: { + name, + }, }), }, rowProps: () => ({ diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 38f04d41ec2f0..aa80184194c94 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -21280,7 +21280,6 @@ "xpack.idxMgmt.componentTemplatesList.table.deleteActionLabel": "Supprimer", "xpack.idxMgmt.componentTemplatesList.table.deleteComponentTemplatesButtonLabel": "Supprimer {count, plural, one {le modèle de composant} other {les modèles de composants} }", "xpack.idxMgmt.componentTemplatesList.table.deprecatedFilterLabel": "Déclassé", - "xpack.idxMgmt.componentTemplatesList.table.disabledSelectionLabel": "Le modèle de composant est en cours d'utilisation, sa suppression est impossible", "xpack.idxMgmt.componentTemplatesList.table.filtersAriaLabel": "Filtres", "xpack.idxMgmt.componentTemplatesList.table.filtersButtonLabel": "Filtres", "xpack.idxMgmt.componentTemplatesList.table.inUseFilterLabel": "En cours d'utilisation", @@ -21291,7 +21290,6 @@ "xpack.idxMgmt.componentTemplatesList.table.nameColumnTitle": "Nom", "xpack.idxMgmt.componentTemplatesList.table.notInUseCellDescription": "Non utilisé", "xpack.idxMgmt.componentTemplatesList.table.reloadButtonLabel": "Recharger", - "xpack.idxMgmt.componentTemplatesList.table.selectionLabel": "Sélectionner ce modèle de composant", "xpack.idxMgmt.componentTemplatesList.table.settingsColumnTitle": "Paramètres", "xpack.idxMgmt.componentTemplatesSelector.emptyPromptDescription": "Les modèles de composants permettent d'enregistrer les paramètres des index, les mappings et les alias, ainsi que d'hériter de ces derniers dans les modèles d'index.", "xpack.idxMgmt.componentTemplatesSelector.emptyPromptLearnMoreLinkText": "En savoir plus.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 042815dfa49e6..9280f3b99b867 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -21270,7 +21270,6 @@ "xpack.idxMgmt.componentTemplatesList.table.deleteActionLabel": "削除", "xpack.idxMgmt.componentTemplatesList.table.deleteComponentTemplatesButtonLabel": "{count, plural, other {個のコンポーネントテンプレート} }を削除", "xpack.idxMgmt.componentTemplatesList.table.deprecatedFilterLabel": "非推奨", - "xpack.idxMgmt.componentTemplatesList.table.disabledSelectionLabel": "コンポーネントテンプレートは使用中であるため、削除できません", "xpack.idxMgmt.componentTemplatesList.table.filtersAriaLabel": "フィルター", "xpack.idxMgmt.componentTemplatesList.table.filtersButtonLabel": "フィルター", "xpack.idxMgmt.componentTemplatesList.table.inUseFilterLabel": "使用中", @@ -21281,7 +21280,6 @@ "xpack.idxMgmt.componentTemplatesList.table.nameColumnTitle": "名前", "xpack.idxMgmt.componentTemplatesList.table.notInUseCellDescription": "使用されていません", "xpack.idxMgmt.componentTemplatesList.table.reloadButtonLabel": "再読み込み", - "xpack.idxMgmt.componentTemplatesList.table.selectionLabel": "このコンポーネントテンプレートを選択", "xpack.idxMgmt.componentTemplatesList.table.settingsColumnTitle": "設定", "xpack.idxMgmt.componentTemplatesSelector.emptyPromptDescription": "コンポーネントテンプレートでは、インデックス設定、マッピング、エイリアスを保存し、インデックステンプレートでそれらを継承できます。", "xpack.idxMgmt.componentTemplatesSelector.emptyPromptLearnMoreLinkText": "詳細情報", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 21d07483427db..08826a2d5775e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -21298,7 +21298,6 @@ "xpack.idxMgmt.componentTemplatesList.table.deleteActionLabel": "删除", "xpack.idxMgmt.componentTemplatesList.table.deleteComponentTemplatesButtonLabel": "删除{count, plural, other {组件模板} }", "xpack.idxMgmt.componentTemplatesList.table.deprecatedFilterLabel": "(已过时)", - "xpack.idxMgmt.componentTemplatesList.table.disabledSelectionLabel": "组件模板正在使用中,无法删除", "xpack.idxMgmt.componentTemplatesList.table.filtersAriaLabel": "筛选", "xpack.idxMgmt.componentTemplatesList.table.filtersButtonLabel": "筛选", "xpack.idxMgmt.componentTemplatesList.table.inUseFilterLabel": "在使用中", @@ -21309,7 +21308,6 @@ "xpack.idxMgmt.componentTemplatesList.table.nameColumnTitle": "名称", "xpack.idxMgmt.componentTemplatesList.table.notInUseCellDescription": "未在使用中", "xpack.idxMgmt.componentTemplatesList.table.reloadButtonLabel": "重新加载", - "xpack.idxMgmt.componentTemplatesList.table.selectionLabel": "选择此组件模板", "xpack.idxMgmt.componentTemplatesList.table.settingsColumnTitle": "设置", "xpack.idxMgmt.componentTemplatesSelector.emptyPromptDescription": "组件模板允许您保存索引设置、映射和别名并在索引模板中继承它们。", "xpack.idxMgmt.componentTemplatesSelector.emptyPromptLearnMoreLinkText": "了解详情。", From c1b7d8254e571cc15d6a2c44d8b4ec0b3c4a11b8 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Fri, 13 Sep 2024 15:37:30 +0200 Subject: [PATCH 16/17] [ML] Functional tests - stabilize model list tests (#192851) ## Summary This PR stabilizes the model list tests by adding a wait for global loading before checking the collapsed action button. ### Details I was able to reproduce the original failure by running the test suite in my local browser with network speed throttled to 3G: after stopping a model deployment, the row actions change and during that time the global loading is active. Checking the collapsed action button too early (i.e. before global loading finished) returns a false positive and then the next step will try to click that button which in the mean time disappeared. Adding the wait for global loading before checking the collapsed actions button fixed for my local runs with throttling. Closes #175443 --- .../apps/ml/short_tests/model_management/model_list.ts | 3 +-- x-pack/test/functional/services/ml/trained_models_table.ts | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts b/x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts index 315b8590a80d1..b7180fcefdc31 100644 --- a/x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts +++ b/x-pack/test/functional/apps/ml/short_tests/model_management/model_list.ts @@ -477,8 +477,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.navigation.navigateToTrainedModels(); }); - // FLAKY: https://github.com/elastic/kibana/issues/175443 - describe.skip('with imported models', function () { + describe('with imported models', function () { before(async () => { await ml.navigation.navigateToTrainedModels(); }); diff --git a/x-pack/test/functional/services/ml/trained_models_table.ts b/x-pack/test/functional/services/ml/trained_models_table.ts index b1a686b411c70..7470402503bfc 100644 --- a/x-pack/test/functional/services/ml/trained_models_table.ts +++ b/x-pack/test/functional/services/ml/trained_models_table.ts @@ -23,7 +23,7 @@ export interface TrainedModelRowData { export type MlTrainedModelsTable = ProvidedType; export function TrainedModelsTableProvider( - { getService }: FtrProviderContext, + { getPageObject, getService }: FtrProviderContext, mlCommonUI: MlCommonUI, trainedModelsActions: TrainedModelsActions ) { @@ -31,6 +31,7 @@ export function TrainedModelsTableProvider( const retry = getService('retry'); const find = getService('find'); const browser = getService('browser'); + const headerPage = getPageObject('header'); return new (class ModelsTable { public async parseModelsTable() { @@ -194,6 +195,7 @@ export function TrainedModelsTableProvider( } public async doesModelCollapsedActionsButtonExist(modelId: string): Promise { + await headerPage.waitUntilLoadingHasFinished(); return await testSubjects.exists(this.rowSelector(modelId, 'euiCollapsedItemActionsButton')); } From 25225c3da8b14f8d33774f4862c885b2fb6523fe Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:40:57 -0400 Subject: [PATCH 17/17] [Security Solution][Endpoint] Add support for spaces to DEV CLI scripts (#192525) ## Summary The following changes were made to dev's CLI tooling: - support for space ID (`--spaceId`) was added to the following CLI [dev scripts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint): - `run_endpoint_agent.js` - `run_sentinelone_host.js` - Support API key (`--apiKey`) was dded to the `run_sentinelone_host.js` CLI dev script (can now run script against cloud intances) - Fleet Agent Policies created via our scripting common methods will now set the `namespace` on the agent policy to match the active space - A few areas of scripting were also updated so that Integration Policies no longer define a `namespace`, thus allowing for it to default to the Agent Policy `namespace` value - SentinelOne SIEM rule, created when the `run_sentinelone_host` script is run, will now set the namespace on the index patterns (retrieved from the integration policy created) - Ensures that the rule only pulls data from the scope (`namespace`) the integration policy is setup with - The space id was added to the VM name when creating an Endpoint or Sentinelone VM (will help to identify what space a VM belongs to) --- .../endpoint/common/endpoint_host_services.ts | 5 +- .../fleet_server/fleet_server_services.ts | 74 +++++++++++----- .../scripts/endpoint/common/fleet_services.ts | 31 +++++-- .../scripts/endpoint/common/spaces.ts | 85 +++++++++++++++++++ .../scripts/endpoint/common/stack_services.ts | 50 ++++++++++- .../endpoint_agent_runner/elastic_endpoint.ts | 5 +- .../endpoint/endpoint_agent_runner/index.ts | 19 ++++- .../endpoint/endpoint_agent_runner/runtime.ts | 2 + .../endpoint/endpoint_agent_runner/setup.ts | 9 +- .../endpoint/endpoint_agent_runner/types.ts | 1 + .../endpoint/sentinelone_host/common.ts | 16 +++- .../endpoint/sentinelone_host/index.ts | 35 +++++++- 12 files changed, 287 insertions(+), 45 deletions(-) create mode 100644 x-pack/plugins/security_solution/scripts/endpoint/common/spaces.ts diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_host_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_host_services.ts index f56073f4b5bc3..d065df1f86a04 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_host_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/endpoint_host_services.ts @@ -8,6 +8,7 @@ import { kibanaPackageJson } from '@kbn/repo-info'; import type { KbnClient } from '@kbn/test'; import type { ToolingLog } from '@kbn/tooling-log'; +import { fetchActiveSpace } from './spaces'; import { isServerlessKibanaFlavor } from '../../../common/endpoint/utils/kibana_status'; import { fetchFleetLatestAvailableAgentVersion } from '../../../common/endpoint/utils/fetch_fleet_version'; import { prefixedOutputLogger } from './utils'; @@ -66,8 +67,10 @@ export const createAndEnrollEndpointHost = async ({ agentVersion = await fetchFleetLatestAvailableAgentVersion(kbnClient); } } + const activeSpaceId = (await fetchActiveSpace(kbnClient)).id; const isRunningInCI = Boolean(process.env.CI); - const vmName = hostname ?? `test-host-${Math.random().toString().substring(2, 6)}`; + const vmName = + hostname ?? `test-host-${activeSpaceId}-${Math.random().toString().substring(2, 6)}`; const { url: agentUrl } = await getAgentDownloadUrl(agentVersion, useClosestVersionMatch, log); const agentDownload = isRunningInCI ? await downloadAndStoreAgent(agentUrl) : undefined; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts index 2a429bd3fac7f..51260c5ac6053 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts @@ -11,9 +11,8 @@ import execa from 'execa'; import chalk from 'chalk'; import assert from 'assert'; import pRetry from 'p-retry'; -import type { AgentPolicy, CreateAgentPolicyResponse, Output } from '@kbn/fleet-plugin/common'; +import type { Output } from '@kbn/fleet-plugin/common'; import { - AGENT_POLICY_API_ROUTES, API_VERSIONS, FLEET_SERVER_PACKAGE, PACKAGE_POLICY_SAVED_OBJECT_TYPE, @@ -47,18 +46,23 @@ import { createToolingLogger } from '../../../../common/endpoint/data_loaders/ut import type { FormattedAxiosError } from '../../../../common/endpoint/format_axios_error'; import { catchAxiosErrorFormatAndThrow } from '../../../../common/endpoint/format_axios_error'; import { + createAgentPolicy, + createIntegrationPolicy, ensureFleetSetup, fetchFleetOutputs, fetchFleetServerHostList, fetchFleetServerUrl, fetchIntegrationPolicyList, + fetchPackageInfo, generateFleetServiceToken, getAgentVersionMatchingCurrentStack, getFleetElasticsearchOutputHost, + randomAgentPolicyName, waitForHostToEnroll, } from '../fleet_services'; import { getLocalhostRealIp } from '../network_services'; import { isLocalhost } from '../is_localhost'; +import { fetchActiveSpace } from '../spaces'; export const FLEET_SERVER_CUSTOM_CONFIG = resolve(__dirname, './fleet_server.yml'); @@ -195,30 +199,54 @@ const getOrCreateFleetServerAgentPolicyId = async ( log.info(`Creating new Fleet Server policy`); - const createdFleetServerPolicy: AgentPolicy = await kbnClient - .request({ - method: 'POST', - path: AGENT_POLICY_API_ROUTES.CREATE_PATTERN, - headers: { 'elastic-api-version': '2023-10-31' }, - body: { - name: `Fleet Server policy (${Math.random().toString(32).substring(2)})`, - description: `Created by CLI Tool via: ${__filename}`, - namespace: 'default', - monitoring_enabled: ['logs', 'metrics'], - // This will ensure the Fleet Server integration policy - // is also created and added to the agent policy - has_fleet_server: true, + const [activeSpaceId, fleetServerPackageInfo] = await Promise.all([ + fetchActiveSpace(kbnClient).then((space) => space.id), + fetchPackageInfo(kbnClient, 'fleet_server'), + ]); + + const agentPolicy = await createAgentPolicy({ + kbnClient, + policy: { + namespace: activeSpaceId, + name: randomAgentPolicyName('Fleet server'), + monitoring_enabled: ['logs', 'metrics'], + }, + }); + + log.verbose(agentPolicy); + + const fleetServerIntegrationPolicy = await createIntegrationPolicy(kbnClient, { + name: randomAgentPolicyName('Fleet server integration'), + description: `Created from script at [${__filename}]`, + namespace: activeSpaceId, + policy_ids: [agentPolicy.id], + enabled: true, + force: false, + inputs: [ + { + type: 'fleet-server', + policy_template: 'fleet_server', + enabled: true, + streams: [], + vars: { + max_agents: { type: 'integer' }, + max_connections: { type: 'integer' }, + custom: { value: '', type: 'yaml' }, + }, }, - }) - .then((response) => response.data.item) - .catch(catchAxiosErrorFormatAndThrow); + ], + package: { + name: 'fleet_server', + title: fleetServerPackageInfo.title, + version: fleetServerPackageInfo.version, + }, + }); + + log.verbose(fleetServerIntegrationPolicy); - log.info( - `Agent Policy created: ${createdFleetServerPolicy.name} (${createdFleetServerPolicy.id})` - ); - log.verbose(createdFleetServerPolicy); + log.info(`Agent Policy created: ${agentPolicy.name} (${agentPolicy.id})`); - return createdFleetServerPolicy.id; + return agentPolicy.id; }); }; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts index 5ad28bc37566c..92c13a521ed2c 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_services.ts @@ -62,6 +62,7 @@ import semver from 'semver'; import axios from 'axios'; import { userInfo } from 'os'; import pRetry from 'p-retry'; +import { fetchActiveSpace } from './spaces'; import { fetchKibanaStatus } from '../../../common/endpoint/utils/kibana_status'; import { isFleetServerRunning } from './fleet_server/fleet_server_services'; import { getEndpointPackageInfo } from '../../../common/endpoint/utils/package'; @@ -83,11 +84,14 @@ const DEFAULT_AGENT_POLICY_NAME = `${CURRENT_USERNAME} test policy`; /** A Fleet agent policy that includes integrations that don't actually require an agent to run on a host. Example: SenttinelOne */ export const DEFAULT_AGENTLESS_INTEGRATIONS_AGENT_POLICY_NAME = `${CURRENT_USERNAME} - agentless integrations`; -const randomAgentPolicyName = (() => { +/** + * Generate a random policy name + */ +export const randomAgentPolicyName = (() => { let counter = fleetGenerator.randomN(100); - return (): string => { - return `agent policy - ${fleetGenerator.randomString(10)}_${counter++}`; + return (prefix: string = 'agent policy'): string => { + return `${prefix} - ${fleetGenerator.randomString(10)}_${counter++}`; }; })(); @@ -814,7 +818,7 @@ export const createAgentPolicy = async ({ const body: CreateAgentPolicyRequest['body'] = policy ?? { name: randomAgentPolicyName(), description: `Policy created by security solution tooling: ${__filename}`, - namespace: 'default', + namespace: (await fetchActiveSpace(kbnClient)).id, monitoring_enabled: ['logs', 'metrics'], }; @@ -862,12 +866,13 @@ export const getOrCreateDefaultAgentPolicy = async ({ log.info(`Creating default test/dev Fleet agent policy with name: [${policyName}]`); + const spaceId = (await fetchActiveSpace(kbnClient)).id; const newAgentPolicy = await createAgentPolicy({ kbnClient, policy: { name: policyName, description: `Policy created by security solution tooling: ${__filename}`, - namespace: 'default', + namespace: spaceId, monitoring_enabled: ['logs', 'metrics'], }, }); @@ -989,7 +994,6 @@ export const addSentinelOneIntegrationToAgentPolicy = async ({ return createIntegrationPolicy(kbnClient, { name: integrationPolicyName, description: `Created by script: ${__filename}`, - namespace: 'default', policy_id: agentPolicyId, policy_ids: [agentPolicyId], enabled: true, @@ -1217,7 +1221,6 @@ export const addEndpointIntegrationToAgentPolicy = async ({ const newIntegrationPolicy = await createIntegrationPolicy(kbnClient, { name, description: `Created by: ${__filename}`, - namespace: 'default', policy_id: agentPolicyId, policy_ids: [agentPolicyId], enabled: true, @@ -1352,3 +1355,17 @@ export const fetchAllEndpointIntegrationPolicyListIds = async ( return policyIds; }; + +/** + * Calls the Fleet internal API to enable space awareness + * @param kbnClient + */ +export const enableFleetSpaceAwareness = memoize(async (kbnClient: KbnClient): Promise => { + await kbnClient + .request({ + path: '/internal/fleet/enable_space_awareness', + headers: { 'Elastic-Api-Version': '1' }, + method: 'POST', + }) + .catch(catchAxiosErrorFormatAndThrow); +}); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/spaces.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/spaces.ts new file mode 100644 index 0000000000000..8b36aaeff59fd --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/spaces.ts @@ -0,0 +1,85 @@ +/* + * 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 type { KbnClient } from '@kbn/test'; +import { AxiosError } from 'axios'; +import type { ToolingLog } from '@kbn/tooling-log'; +import type { Space } from '@kbn/spaces-plugin/common'; +import { DEFAULT_SPACE_ID, getSpaceIdFromPath } from '@kbn/spaces-plugin/common'; +import { memoize } from 'lodash'; +import { createToolingLogger } from '../../../common/endpoint/data_loaders/utils'; +import { catchAxiosErrorFormatAndThrow } from '../../../common/endpoint/format_axios_error'; + +/** + * Check that a given space id exists in Kibana and created it if not. + */ +export const ensureSpaceIdExists = async ( + kbnClient: KbnClient, + /** If space id is not defined, it will be derived from the `KbnClient` kibana url */ + spaceId: string = getSpaceIdFromKbnClientUrl(kbnClient).spaceId, + { log = createToolingLogger() }: { log?: ToolingLog } = {} +): Promise => { + if (!spaceId || spaceId === DEFAULT_SPACE_ID) { + return; + } + + const alreadyExists = await kbnClient.spaces + .get(spaceId) + .then(() => { + log.debug(`Space id [${spaceId}] already exists. Nothing to do.`); + return true; + }) + .catch((err) => { + if (err instanceof AxiosError && (err.response?.status ?? err.status) === 404) { + return false; + } + + throw err; + }) + .catch(catchAxiosErrorFormatAndThrow); + + if (!alreadyExists) { + log.info(`Creating space id [${spaceId}]`); + + await kbnClient.spaces + .create({ + name: spaceId, + id: spaceId, + }) + .catch(catchAxiosErrorFormatAndThrow); + } +}; + +/** + * Get the current active space for the provided KbnClient + * + * NOTE: this utility may generate a `404` error if the `KbnClient` has been + * initialized for a specific space, but that space does not yet exist. + * + * @param kbnClient + */ +export const fetchActiveSpace = memoize(async (kbnClient: KbnClient): Promise => { + return kbnClient + .request({ + method: 'GET', + path: `/internal/spaces/_active_space`, + }) + .catch(catchAxiosErrorFormatAndThrow) + .then((response) => response.data); +}); + +/** + * Returns the space id that the provided KbnClient was initialized for by parsting its url + * @param kbnClient + */ +export const getSpaceIdFromKbnClientUrl = ( + kbnClient: KbnClient +): ReturnType => { + const newUrl = new URL(kbnClient.resolveUrl('/')); + + return getSpaceIdFromPath(newUrl.pathname); // NOTE: we are not currently supporting a Kibana base path prefix +}; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts index 3ec0c0f230423..88da087336d3f 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts @@ -16,6 +16,8 @@ import type { ClientOptions } from '@elastic/elasticsearch/lib/client'; import fs from 'fs'; import { CA_CERT_PATH } from '@kbn/dev-utils'; import { omit } from 'lodash'; +import { addSpaceIdToPath, DEFAULT_SPACE_ID, getSpaceIdFromPath } from '@kbn/spaces-plugin/common'; +import { enableFleetSpaceAwareness } from './fleet_services'; import { fetchKibanaStatus, isServerlessKibanaFlavor, @@ -63,6 +65,8 @@ interface CreateRuntimeServicesOptions { fleetServerUrl?: string; username: string; password: string; + /** The space id in kibana */ + spaceId?: string; /** If defined, both `username` and `password` will be ignored */ apiKey?: string; /** If undefined, ES username defaults to `username` */ @@ -107,11 +111,12 @@ class KbnClientExtended extends KbnClient { } export const createRuntimeServices = async ({ - kibanaUrl, + kibanaUrl: _kibanaUrl, elasticsearchUrl, fleetServerUrl = 'https://localhost:8220', username: _username, password: _password, + spaceId, apiKey, esUsername: _esUsername, esPassword: _esPassword, @@ -119,6 +124,7 @@ export const createRuntimeServices = async ({ asSuperuser = false, useCertForSsl = false, }: CreateRuntimeServicesOptions): Promise => { + const kibanaUrl = spaceId ? buildUrlWithSpaceId(_kibanaUrl, spaceId) : _kibanaUrl; let username = _username; let password = _password; let esUsername = _esUsername; @@ -131,6 +137,7 @@ export const createRuntimeServices = async ({ password, useCertForSsl, log, + spaceId, }); await waitForKibana(tmpKbnClient); @@ -169,9 +176,23 @@ export const createRuntimeServices = async ({ const kbnURL = new URL(kibanaUrl); const esURL = new URL(elasticsearchUrl); const fleetURL = new URL(fleetServerUrl); + const kbnClient = createKbnClient({ + log, + url: kibanaUrl, + username, + password, + spaceId, + apiKey, + useCertForSsl, + }); + + if (spaceId && spaceId !== DEFAULT_SPACE_ID) { + log?.info(`Enabling Fleet space awareness`); + await enableFleetSpaceAwareness(kbnClient); + } return { - kbnClient: createKbnClient({ log, url: kibanaUrl, username, password, apiKey, useCertForSsl }), + kbnClient, esClient: createEsClient({ log, url: elasticsearchUrl, @@ -265,9 +286,10 @@ export const createEsClient = ({ }; export const createKbnClient = ({ - url, + url: _url, username, password, + spaceId, apiKey, log = createToolingLogger(), useCertForSsl = false, @@ -277,9 +299,11 @@ export const createKbnClient = ({ password: string; /** If defined, both `username` and `password` will be ignored */ apiKey?: string; + spaceId?: string; log?: ToolingLog; useCertForSsl?: boolean; }): KbnClient => { + const url = spaceId ? buildUrlWithSpaceId(_url, spaceId) : _url; const isHttps = new URL(url).protocol.startsWith('https'); const clientOptions: ConstructorParameters[0] = { log, @@ -302,6 +326,26 @@ export const createKbnClient = ({ return new KbnClientExtended(clientOptions); }; +/** + * Builds a new URL based on the one provided on input for the given space id + * @param url + * @param spaceId + */ +export const buildUrlWithSpaceId = (url: string, spaceId: string): string => { + const newUrl = new URL(url); + let requestPath = newUrl.pathname; + const currentUrlSpace = getSpaceIdFromPath(requestPath); // NOTE: we are not currently supporting a Kibana base path prefix + + if (currentUrlSpace.pathHasExplicitSpaceIdentifier) { + // Get the request path (if any) from the url + requestPath = requestPath.substring(`/s/${currentUrlSpace.spaceId}`.length) || '/'; + } + + newUrl.pathname = addSpaceIdToPath('/', spaceId, requestPath); + + return newUrl.href; +}; + /** * Retrieves the Stack (kibana/ES) version from the `/api/status` kibana api * @param kbnClient diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts index 42aa3610a0124..e18f91457eca7 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/elastic_endpoint.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { fetchActiveSpace } from '../common/spaces'; import { dump } from '../common/utils'; import { generateVmName } from '../common/vm_services'; import { createAndEnrollEndpointHost } from '../common/endpoint_host_services'; @@ -37,7 +38,9 @@ export const enrollEndpointHost = async (): Promise => { throw new Error(`No 'version' specified`); } - vmName = generateVmName('dev'); + const activeSpaceId = (await fetchActiveSpace(kbnClient)).id; + + vmName = generateVmName(`dev-${activeSpaceId}`); log.info(`Creating VM named: ${vmName}`); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/index.ts index 03d0b14f9852c..a0cfee1f98fdc 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/index.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/index.ts @@ -7,6 +7,7 @@ import type { RunFn } from '@kbn/dev-cli-runner'; import { run } from '@kbn/dev-cli-runner'; +import { createToolingLogger } from '../../../common/endpoint/data_loaders/utils'; import { setupAll } from './setup'; const runSetupAll: RunFn = async (cliContext) => { @@ -15,11 +16,14 @@ const runSetupAll: RunFn = async (cliContext) => { const apiKey = cliContext.flags.apiKey as string; const kibanaUrl = cliContext.flags.kibanaUrl as string; const elasticUrl = cliContext.flags.elasticUrl as string; + const spaceId = cliContext.flags.spaceId as string; const fleetServerUrl = cliContext.flags.fleetServerUrl as string; const version = cliContext.flags.version as string; const policy = cliContext.flags.policy as string; const log = cliContext.log; + createToolingLogger.setDefaultLogLevelFromCliFlags(cliContext.flags); + await setupAll({ elasticUrl, kibanaUrl, @@ -30,6 +34,7 @@ const runSetupAll: RunFn = async (cliContext) => { version, policy, log, + spaceId, }); }; @@ -45,7 +50,16 @@ export const cli = () => { 'multipass', install Elastic Agent and enroll it with Fleet. Can be used multiple times against the same stack.`, flags: { - string: ['kibana', 'elastic', 'username', 'password', 'version', 'policy', 'apiKey'], + string: [ + 'kibana', + 'elastic', + 'username', + 'password', + 'version', + 'policy', + 'apiKey', + 'spaceId', + ], default: { kibanaUrl: 'http://127.0.0.1:5601', elasticUrl: 'http://127.0.0.1:9200', @@ -54,6 +68,7 @@ export const cli = () => { apiKey: '', version: '', policy: '', + spaceId: '', }, help: ` --version Optional. The version of the Agent to use for enrolling the new host. @@ -67,6 +82,8 @@ export const cli = () => { --password Optional. Password associated with the username (Default: changeme) --apiKey Optional. A Kibana API key to use for authz. When defined, 'username' and 'password' arguments are ignored. + --spaceId Optional. The space id where the host should be added to in kibana. The + space will be created if it does not exist. Default: default space. --kibanaUrl Optional. The url to Kibana (Default: http://127.0.0.1:5601) --elasticUrl Optional. The url to Elasticsearch (Default: http://127.0.0.1:9200) `, diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/runtime.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/runtime.ts index 36fbf86947013..62010e733deba 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/runtime.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/runtime.ts @@ -29,6 +29,7 @@ export const startRuntimeServices = async ({ username, password, apiKey, + spaceId, ...otherOptions }: StartRuntimeServicesOptions) => { const stackServices = await createRuntimeServices({ @@ -39,6 +40,7 @@ export const startRuntimeServices = async ({ password, apiKey, log, + spaceId, asSuperuser: otherOptions?.asSuperuser, }); diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/setup.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/setup.ts index c851b6ee34dbe..e8cd386e56f31 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/setup.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/setup.ts @@ -5,17 +5,24 @@ * 2.0. */ +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; +import { ensureSpaceIdExists } from '../common/spaces'; import { getRuntimeServices, startRuntimeServices, stopRuntimeServices } from './runtime'; import { checkDependencies } from './pre_check'; import { enrollEndpointHost } from './elastic_endpoint'; import type { StartRuntimeServicesOptions } from './types'; import { startFleetServerIfNecessary } from '../common/fleet_server/fleet_server_services'; +import { enableFleetSpaceAwareness } from '../common/fleet_services'; export const setupAll = async (options: StartRuntimeServicesOptions) => { await startRuntimeServices(options); - const { kbnClient, log } = getRuntimeServices(); + if (options.spaceId && options.spaceId !== DEFAULT_SPACE_ID) { + await enableFleetSpaceAwareness(kbnClient); + await ensureSpaceIdExists(kbnClient, options.spaceId); + } + await checkDependencies(); await startFleetServerIfNecessary({ diff --git a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/types.ts b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/types.ts index 673b0d6b3441d..d9d313d11f49c 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/types.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/endpoint_agent_runner/types.ts @@ -13,6 +13,7 @@ export interface StartRuntimeServicesOptions { fleetServerUrl?: string; username: string; password: string; + spaceId?: string; apiKey?: string; version?: string; policy?: string; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/common.ts b/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/common.ts index 6e829256e3c1c..dcc3b303ce5b2 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/common.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/common.ts @@ -11,6 +11,7 @@ import axios from 'axios'; import type { KbnClient } from '@kbn/test'; import { SENTINELONE_CONNECTOR_ID } from '@kbn/stack-connectors-plugin/common/sentinelone/constants'; import pRetry from 'p-retry'; +import { fetchActiveSpace } from '../common/spaces'; import { dump } from '../common/utils'; import { type RuleResponse } from '../../../common/api/detection_engine'; import { createToolingLogger } from '../../../common/endpoint/data_loaders/utils'; @@ -21,7 +22,6 @@ import type { } from './types'; import { catchAxiosErrorFormatAndThrow } from '../../../common/endpoint/format_axios_error'; import type { HostVm } from '../common/types'; - import { createConnector, fetchConnectorByType } from '../common/connectors_services'; import { createRule, findRules } from '../common/detection_rules_services'; @@ -245,8 +245,10 @@ export const createSentinelOneStackConnectorIfNeeded = async ({ log, s1ApiToken, s1Url, - name = 'SentinelOne Dev instance', + name: _name, }: CreateSentinelOneStackConnectorIfNeededOptions): Promise => { + const name = + _name ?? `SentinelOne Dev instance (space: ${(await fetchActiveSpace(kbnClient)).id})`; const connector = await fetchConnectorByType(kbnClient, SENTINELONE_CONNECTOR_ID); if (connector) { @@ -271,11 +273,17 @@ export const createSentinelOneStackConnectorIfNeeded = async ({ export const createDetectionEngineSentinelOneRuleIfNeeded = async ( kbnClient: KbnClient, - log: ToolingLog + log: ToolingLog, + /** If defined, then the Index patterns used the SIEM rule will include this value */ + namespace?: string ): Promise => { const ruleName = 'Promote SentinelOne alerts'; const tag = 'dev-script-run-sentinelone-host'; - const index = ['logs-sentinel_one.alert*', 'logs-sentinel_one.threat*']; + const indexNamespace = namespace ? `-${namespace}` : ''; + const index = [ + `logs-sentinel_one.alert${indexNamespace}*`, + `logs-sentinel_one.threat${indexNamespace}*`, + ]; const ruleQueryValue = 'sentinel_one.alert.agent.id:* OR sentinel_one.threat.agent.id:*'; const { data } = await findRules(kbnClient, { diff --git a/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts index d2f4190821413..bfc72f61a5ae8 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/sentinelone_host/index.ts @@ -8,6 +8,8 @@ import type { RunFn } from '@kbn/dev-cli-runner'; import { run } from '@kbn/dev-cli-runner'; import { ok } from 'assert'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; +import { ensureSpaceIdExists, fetchActiveSpace } from '../common/spaces'; import { isFleetServerRunning, startFleetServer, @@ -17,6 +19,7 @@ import { createToolingLogger } from '../../../common/endpoint/data_loaders/utils import { addSentinelOneIntegrationToAgentPolicy, DEFAULT_AGENTLESS_INTEGRATIONS_AGENT_POLICY_NAME, + enableFleetSpaceAwareness, enrollHostVmWithFleet, fetchAgentPolicy, getOrCreateDefaultAgentPolicy, @@ -56,6 +59,8 @@ console and pushes the data to Elasticsearch.`, 's1Url', 's1ApiToken', 'vmName', + 'spaceId', + 'apiKey', ], boolean: ['forceFleetServer', 'forceNewS1Host'], default: { @@ -63,6 +68,8 @@ console and pushes the data to Elasticsearch.`, username: 'elastic', password: 'changeme', policy: '', + spaceId: '', + apiKey: '', }, help: ` --s1Url Required. The base URL for SentinelOne management console. @@ -82,6 +89,10 @@ console and pushes the data to Elasticsearch.`, --username Optional. User name to be used for auth against elasticsearch and kibana (Default: elastic). --password Optional. Password associated with the username (Default: changeme) + --apiKey Optional. A Kibana API key to use for authz. When defined, 'username' + and 'password' arguments are ignored. + --spaceId Optional. The space id where the host should be added to in kibana. The + space will be created if it does not exist. Default: default space. --kibanaUrl Optional. The url to Kibana (Default: http://127.0.0.1:5601) `, }, @@ -95,6 +106,8 @@ const runCli: RunFn = async ({ log, flags }) => { const s1Url = flags.s1Url as string; const s1ApiToken = flags.s1ApiToken as string; const policy = flags.policy as string; + const spaceId = flags.spaceId as string; + const apiKey = flags.apiKey as string; const forceFleetServer = flags.forceFleetServer as boolean; const forceNewS1Host = flags.forceNewS1Host as boolean; const getRequiredArgMessage = (argName: string) => `${argName} argument is required`; @@ -111,8 +124,17 @@ const runCli: RunFn = async ({ log, flags }) => { url: kibanaUrl, username, password, + spaceId, + apiKey, }); + if (spaceId && spaceId !== DEFAULT_SPACE_ID) { + await ensureSpaceIdExists(kbnClient, spaceId, { log }); + await enableFleetSpaceAwareness(kbnClient); + } + + const activeSpaceId = (await fetchActiveSpace(kbnClient)).id; + const runningS1VMs = ( await findVm( 'multipass', @@ -152,15 +174,16 @@ const runCli: RunFn = async ({ log, flags }) => { id: agentPolicyId, agents = 0, name: agentPolicyName, + namespace: agentPolicyNamespace, } = policy ? await fetchAgentPolicy(kbnClient, policy) : await getOrCreateDefaultAgentPolicy({ kbnClient, log, - policyName: DEFAULT_AGENTLESS_INTEGRATIONS_AGENT_POLICY_NAME, + policyName: `${DEFAULT_AGENTLESS_INTEGRATIONS_AGENT_POLICY_NAME} - ${activeSpaceId}`, }); - await addSentinelOneIntegrationToAgentPolicy({ + const { namespace: integrationPolicyNamespace } = await addSentinelOneIntegrationToAgentPolicy({ kbnClient, log, agentPolicyId, @@ -176,7 +199,7 @@ const runCli: RunFn = async ({ log, flags }) => { agentPolicyVm = await createVm({ type: 'multipass', - name: generateVmName('agentless-integrations'), + name: generateVmName(`agentless-integrations-${activeSpaceId}`), }); if (forceFleetServer || !(await isFleetServerRunning(kbnClient, log))) { @@ -201,7 +224,11 @@ const runCli: RunFn = async ({ log, flags }) => { await Promise.all([ createSentinelOneStackConnectorIfNeeded({ kbnClient, log, s1ApiToken, s1Url }), - createDetectionEngineSentinelOneRuleIfNeeded(kbnClient, log), + createDetectionEngineSentinelOneRuleIfNeeded( + kbnClient, + log, + integrationPolicyNamespace || agentPolicyNamespace + ), ]); // Trigger an alert on the SentinelOn host so that we get an alert back in Kibana