From 5431d1fb78011385813384ca57c5aebf124afaea Mon Sep 17 00:00:00 2001
From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com>
Date: Wed, 2 Nov 2022 09:48:22 +0100
Subject: [PATCH] Prerelease toggle (#143853)
* WIP: prerelease toggle
* changed styling
* fixed switch
* auto upgrade and hiding button on callout if no ga available
* tweak prereleaseIntegrationsEnabled state to add undefined state in beginning
* fixing types and tests
* fixing types
* removed dummy endpoint package
* extracted hooks to avoid double loading of packages and categories
* prevent double loading of package details
* updated openapi
* fixing tests
* added try catch around loading settings in preconfig
* error handling on integrations, fixing cypress test with that
* reading prerelease from settings during package install
* fix tests
* fixing tests
* added back experimental as deprecated, fix more tests
* fixed issue in package details overview where nlatest version didnt show prerelease
* changed getPackageInfo to load prerelease from settings if not provided
* fixing tests, moved getSettings to bulk install fn
* fixing cypress and endpoint tests
* fix tests
* fix tests
* added back experimental flag in other plugins, as it is not exaclty the same as prerelease
* reverted mappings change in api_integration
* fixed prerelease condition, fix limited test, trying to fix field limit
* removed experimental flag from epr api call
* added unit test on version dropdown and prerelease callout, set field limit to 1500
* added UI package version check for prerelease disabled case
* fixed synthetics test
* extracted getSettings to a helper function
* removed using prerease setting in auto upgrades and install
* fixing tests, added back prerelease flag to install apis
* fixing a bug with version and release badge of installed integrations
* reload package in overview after loading prerelease setting, this is to show available upgrade if package is installed
* fixing tests by passing prerelease flag on apis
* fixing cypress test
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../migrations/check_registered_types.test.ts | 2 +-
.../plugins/fleet/common/openapi/bundled.json | 58 +++++++-
.../plugins/fleet/common/openapi/bundled.yaml | 40 +++++
.../openapi/components/schemas/settings.yaml | 2 +
.../common/openapi/paths/epm@categories.yaml | 19 +++
.../common/openapi/paths/epm@packages.yaml | 17 +++
.../fleet/common/types/models/settings.ts | 1 +
.../fleet/common/types/rest_spec/epm.ts | 4 +
.../fleet/cypress/e2e/install_assets.cy.ts | 2 +-
.../fleet/cypress/e2e/integrations_mock.cy.ts | 6 +-
.../fleet/cypress/e2e/integrations_real.cy.ts | 2 +-
.../debug/components/integration_debugger.tsx | 2 +-
.../applications/integrations/hooks/index.ts | 2 +
.../integrations/hooks/use_categories.tsx | 55 +++++++
.../integrations/hooks/use_packages.tsx | 56 +++++++
.../integration_preference.stories.tsx | 8 +-
.../epm/components/integration_preference.tsx | 67 ++++++++-
.../epm/screens/detail/index.test.tsx | 75 +++++++++-
.../sections/epm/screens/detail/index.tsx | 116 ++++++++++++++-
.../epm/screens/detail/overview/overview.tsx | 140 ++++++++++++------
.../epm/screens/home/available_packages.tsx | 31 ++--
.../sections/epm/screens/home/index.tsx | 25 +++-
.../hooks/use_package_installations.tsx | 2 +-
.../fleet/public/hooks/use_request/epm.ts | 35 ++++-
x-pack/plugins/fleet/public/services/index.ts | 1 +
.../services/package_prerelease.test.ts | 34 +++++
.../public/services/package_prerelease.ts | 11 ++
.../install_all_packages.ts | 2 +-
x-pack/plugins/fleet/server/plugin.ts | 2 +-
.../fleet/server/routes/epm/handlers.ts | 22 ++-
.../server/routes/package_policy/handlers.ts | 1 +
.../fleet/server/saved_objects/index.ts | 1 +
.../saved_objects/migrations/to_v8_6_0.ts | 2 +
.../services/epm/package_service.test.ts | 2 +-
.../server/services/epm/package_service.ts | 13 +-
.../epm/packages/bulk_install_packages.ts | 4 +-
.../server/services/epm/packages/get.test.ts | 7 +
.../fleet/server/services/epm/packages/get.ts | 19 ++-
.../epm/packages/get_prerelease_setting.ts | 25 ++++
.../server/services/epm/packages/install.ts | 4 +-
.../server/services/epm/registry/index.ts | 28 +++-
.../fleet/server/services/package_policy.ts | 6 +
.../plugins/fleet/server/services/settings.ts | 2 +-
.../fleet/server/types/rest_spec/epm.ts | 20 ++-
.../fleet/server/types/rest_spec/settings.ts | 1 +
.../apis/epm/bulk_upgrade.ts | 8 +-
.../apis/epm/custom_ingest_pipeline.ts | 2 +-
.../fleet_api_integration/apis/epm/delete.ts | 1 +
.../apis/epm/final_pipeline.ts | 2 +-
.../fleet_api_integration/apis/epm/get.ts | 9 +-
.../apis/epm/install_by_upload.ts | 1 +
.../apis/epm/install_error_rollback.ts | 5 +-
.../apis/epm/install_overrides.ts | 1 +
.../apis/epm/install_prerelease.ts | 1 +
.../epm/install_remove_kbn_assets_in_space.ts | 1 +
.../apis/epm/install_remove_multiple.ts | 1 +
.../apis/epm/install_tag_assets.ts | 1 +
.../apis/epm/install_update.ts | 1 +
.../fleet_api_integration/apis/epm/list.ts | 1 +
.../apis/epm/package_install_complete.ts | 1 +
.../fleet_api_integration/apis/epm/setup.ts | 1 +
.../fleet_api_integration/apis/fleet_setup.ts | 2 +-
.../apis/package_policy/delete.ts | 1 +
x-pack/test/fleet_api_integration/helpers.ts | 16 ++
.../maps/group4/geofile_wizard_auto_open.ts | 2 +-
.../services/uptime/synthetics_package.ts | 2 +-
66 files changed, 886 insertions(+), 148 deletions(-)
create mode 100644 x-pack/plugins/fleet/public/applications/integrations/hooks/use_categories.tsx
create mode 100644 x-pack/plugins/fleet/public/applications/integrations/hooks/use_packages.tsx
create mode 100644 x-pack/plugins/fleet/public/services/package_prerelease.test.ts
create mode 100644 x-pack/plugins/fleet/public/services/package_prerelease.ts
create mode 100644 x-pack/plugins/fleet/server/services/epm/packages/get_prerelease_setting.ts
diff --git a/src/core/server/integration_tests/saved_objects/migrations/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/check_registered_types.test.ts
index b1aa1e5df9231..af572532a13e6 100644
--- a/src/core/server/integration_tests/saved_objects/migrations/check_registered_types.test.ts
+++ b/src/core/server/integration_tests/saved_objects/migrations/check_registered_types.test.ts
@@ -99,7 +99,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
"ingest-download-sources": "1e69dabd6db5e320fe08c5bda8f35f29bafc6b54",
"ingest-outputs": "29b867bf7bfd28b1e17c84697dce5c6d078f9705",
"ingest-package-policies": "e8707a8c7821ea085e67c2d213e24efa56307393",
- "ingest_manager_settings": "bb71f20e36a9ac3a2e46d9345e2caa96e7bf8c22",
+ "ingest_manager_settings": "6f36714825cc15ea8d7cda06fde7851611a532b4",
"inventory-view": "bc2bd1e7ec7c186159447ab228d269f22bd39056",
"kql-telemetry": "29544cd7d3b767c5399878efae6bd724d24c03fd",
"legacy-url-alias": "7172dfd54f2e0c89fe263fd7095519b2d826a930",
diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json
index b2ccff5e7188b..ab8747170f760 100644
--- a/x-pack/plugins/fleet/common/openapi/bundled.json
+++ b/x-pack/plugins/fleet/common/openapi/bundled.json
@@ -247,7 +247,35 @@
}
},
"operationId": "get-package-categories"
- }
+ },
+ "parameters": [
+ {
+ "in": "query",
+ "name": "prerelease",
+ "schema": {
+ "type": "boolean",
+ "default": false
+ },
+ "description": "Whether to include prerelease packages in categories count (e.g. beta, rc, preview) "
+ },
+ {
+ "in": "query",
+ "name": "experimental",
+ "deprecated": true,
+ "schema": {
+ "type": "boolean",
+ "default": false
+ }
+ },
+ {
+ "in": "query",
+ "name": "include_policy_templates",
+ "schema": {
+ "type": "boolean",
+ "default": false
+ }
+ }
+ ]
},
"/epm/packages/limited": {
"get": {
@@ -304,6 +332,31 @@
"default": false
},
"description": "Whether to exclude the install status of each package. Enabling this option will opt in to caching for the response via `cache-control` headers. If you don't need up-to-date installation info for a package, and are querying for a list of available packages, providing this flag can improve performance substantially."
+ },
+ {
+ "in": "query",
+ "name": "prerelease",
+ "schema": {
+ "type": "boolean",
+ "default": false
+ },
+ "description": "Whether to return prerelease versions of packages (e.g. beta, rc, preview) "
+ },
+ {
+ "in": "query",
+ "name": "experimental",
+ "deprecated": true,
+ "schema": {
+ "type": "boolean",
+ "default": false
+ }
+ },
+ {
+ "in": "query",
+ "name": "category",
+ "schema": {
+ "type": "string"
+ }
}
]
},
@@ -4206,6 +4259,9 @@
"items": {
"type": "string"
}
+ },
+ "prerelease_integrations_enabled": {
+ "type": "boolean"
}
},
"required": [
diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml
index 139711f13b899..eca789024e4f3 100644
--- a/x-pack/plugins/fleet/common/openapi/bundled.yaml
+++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml
@@ -154,6 +154,26 @@ paths:
schema:
$ref: '#/components/schemas/get_categories_response'
operationId: get-package-categories
+ parameters:
+ - in: query
+ name: prerelease
+ schema:
+ type: boolean
+ default: false
+ description: >-
+ Whether to include prerelease packages in categories count (e.g. beta,
+ rc, preview)
+ - in: query
+ name: experimental
+ deprecated: true
+ schema:
+ type: boolean
+ default: false
+ - in: query
+ name: include_policy_templates
+ schema:
+ type: boolean
+ default: false
/epm/packages/limited:
get:
summary: Packages - Get limited list
@@ -196,6 +216,24 @@ paths:
headers. If you don't need up-to-date installation info for a package,
and are querying for a list of available packages, providing this flag
can improve performance substantially.
+ - in: query
+ name: prerelease
+ schema:
+ type: boolean
+ default: false
+ description: >-
+ Whether to return prerelease versions of packages (e.g. beta, rc,
+ preview)
+ - in: query
+ name: experimental
+ deprecated: true
+ schema:
+ type: boolean
+ default: false
+ - in: query
+ name: category
+ schema:
+ type: string
/epm/packages/_bulk:
post:
summary: Packages - Bulk install
@@ -2617,6 +2655,8 @@ components:
type: array
items:
type: string
+ prerelease_integrations_enabled:
+ type: boolean
required:
- fleet_server_hosts
- id
diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/settings.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/settings.yaml
index 280460771989e..145b598267a0a 100644
--- a/x-pack/plugins/fleet/common/openapi/components/schemas/settings.yaml
+++ b/x-pack/plugins/fleet/common/openapi/components/schemas/settings.yaml
@@ -9,6 +9,8 @@ properties:
type: array
items:
type: string
+ prerelease_integrations_enabled:
+ type: boolean
required:
- fleet_server_hosts
- id
diff --git a/x-pack/plugins/fleet/common/openapi/paths/epm@categories.yaml b/x-pack/plugins/fleet/common/openapi/paths/epm@categories.yaml
index 1f2c3930d6ba3..9a69a930fa988 100644
--- a/x-pack/plugins/fleet/common/openapi/paths/epm@categories.yaml
+++ b/x-pack/plugins/fleet/common/openapi/paths/epm@categories.yaml
@@ -9,3 +9,22 @@ get:
schema:
$ref: ../components/schemas/get_categories_response.yaml
operationId: get-package-categories
+parameters:
+ - in: query
+ name: prerelease
+ schema:
+ type: boolean
+ default: false
+ description: >-
+ Whether to include prerelease packages in categories count (e.g. beta, rc, preview)
+ - in: query
+ name: experimental
+ deprecated: true
+ schema:
+ type: boolean
+ default: false
+ - in: query
+ name: include_policy_templates
+ schema:
+ type: boolean
+ default: false
\ No newline at end of file
diff --git a/x-pack/plugins/fleet/common/openapi/paths/epm@packages.yaml b/x-pack/plugins/fleet/common/openapi/paths/epm@packages.yaml
index 9c29b9d18357c..a6332360283bd 100644
--- a/x-pack/plugins/fleet/common/openapi/paths/epm@packages.yaml
+++ b/x-pack/plugins/fleet/common/openapi/paths/epm@packages.yaml
@@ -20,3 +20,20 @@ parameters:
caching for the response via `cache-control` headers. If you don't need up-to-date installation
info for a package, and are querying for a list of available packages, providing this flag can
improve performance substantially.
+ - in: query
+ name: prerelease
+ schema:
+ type: boolean
+ default: false
+ description: >-
+ Whether to return prerelease versions of packages (e.g. beta, rc, preview)
+ - in: query
+ name: experimental
+ deprecated: true
+ schema:
+ type: boolean
+ default: false
+ - in: query
+ name: category
+ schema:
+ type: string
diff --git a/x-pack/plugins/fleet/common/types/models/settings.ts b/x-pack/plugins/fleet/common/types/models/settings.ts
index 5a33fea910446..c70fa944e6c24 100644
--- a/x-pack/plugins/fleet/common/types/models/settings.ts
+++ b/x-pack/plugins/fleet/common/types/models/settings.ts
@@ -10,6 +10,7 @@ import type { SavedObjectAttributes } from '@kbn/core/public';
export interface BaseSettings {
has_seen_add_data_notice?: boolean;
fleet_server_hosts?: string[];
+ prerelease_integrations_enabled: boolean;
}
export interface Settings extends BaseSettings {
diff --git a/x-pack/plugins/fleet/common/types/rest_spec/epm.ts b/x-pack/plugins/fleet/common/types/rest_spec/epm.ts
index e12bdbb202321..105558e0d0620 100644
--- a/x-pack/plugins/fleet/common/types/rest_spec/epm.ts
+++ b/x-pack/plugins/fleet/common/types/rest_spec/epm.ts
@@ -17,7 +17,9 @@ import type {
export interface GetCategoriesRequest {
query: {
+ // deprecated in 8.6
experimental?: boolean;
+ prerelease?: boolean;
include_policy_templates?: boolean;
};
}
@@ -31,7 +33,9 @@ export interface GetCategoriesResponse {
export interface GetPackagesRequest {
query: {
category?: string;
+ // deprecated in 8.6
experimental?: boolean;
+ prerelease?: boolean;
excludeInstallStatus?: boolean;
};
}
diff --git a/x-pack/plugins/fleet/cypress/e2e/install_assets.cy.ts b/x-pack/plugins/fleet/cypress/e2e/install_assets.cy.ts
index 81ef56a4b1f52..4234df15d861e 100644
--- a/x-pack/plugins/fleet/cypress/e2e/install_assets.cy.ts
+++ b/x-pack/plugins/fleet/cypress/e2e/install_assets.cy.ts
@@ -35,7 +35,7 @@ describe('Install unverified package assets', () => {
}).as('installAssets');
// save mocking out the whole package response, but make it so that fleet server is always uninstalled
- cy.intercept('GET', '/api/fleet/epm/packages/fleet_server', (req) => {
+ cy.intercept('GET', '/api/fleet/epm/packages/fleet_server*', (req) => {
req.continue((res) => {
if (res.body?.item?.savedObject) {
delete res.body.item.savedObject;
diff --git a/x-pack/plugins/fleet/cypress/e2e/integrations_mock.cy.ts b/x-pack/plugins/fleet/cypress/e2e/integrations_mock.cy.ts
index ce207cd3598e2..3095f628599d6 100644
--- a/x-pack/plugins/fleet/cypress/e2e/integrations_mock.cy.ts
+++ b/x-pack/plugins/fleet/cypress/e2e/integrations_mock.cy.ts
@@ -16,7 +16,7 @@ describe('Add Integration - Mock API', () => {
const oldVersion = '0.3.3';
const newVersion = '1.3.4';
beforeEach(() => {
- cy.intercept('/api/fleet/epm/packages?experimental=true', {
+ cy.intercept('/api/fleet/epm/packages?prerelease=true', {
items: [
{
name: 'apache',
@@ -28,7 +28,7 @@ describe('Add Integration - Mock API', () => {
],
});
- cy.intercept(`/api/fleet/epm/packages/apache/${oldVersion}`, {
+ cy.intercept(`/api/fleet/epm/packages/apache/${oldVersion}*`, {
item: {
name: 'apache',
version: oldVersion,
@@ -99,7 +99,7 @@ describe('Add Integration - Mock API', () => {
cy.getBySel(INTEGRATION_POLICIES_UPGRADE_CHECKBOX).uncheck({ force: true });
- cy.intercept(`/api/fleet/epm/packages/apache/${newVersion}`, {
+ cy.intercept(`/api/fleet/epm/packages/apache/${newVersion}*`, {
item: {
name: 'apache',
version: newVersion,
diff --git a/x-pack/plugins/fleet/cypress/e2e/integrations_real.cy.ts b/x-pack/plugins/fleet/cypress/e2e/integrations_real.cy.ts
index c3bee2d758df0..3b7c29561bc93 100644
--- a/x-pack/plugins/fleet/cypress/e2e/integrations_real.cy.ts
+++ b/x-pack/plugins/fleet/cypress/e2e/integrations_real.cy.ts
@@ -174,7 +174,7 @@ describe('Add Integration - Real API', () => {
setupIntegrations();
cy.getBySel(getIntegrationCategories('aws')).click();
cy.getBySel(INTEGRATIONS_SEARCHBAR.BADGE).contains('AWS').should('exist');
- cy.getBySel(INTEGRATION_LIST).find('.euiCard').should('have.length', 30);
+ cy.getBySel(INTEGRATION_LIST).find('.euiCard').should('have.length', 28);
cy.getBySel(INTEGRATIONS_SEARCHBAR.INPUT).clear().type('Cloud');
cy.getBySel(INTEGRATION_LIST).find('.euiCard').should('have.length', 3);
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/integration_debugger.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/integration_debugger.tsx
index 9c3fa21c752f8..30fc1b84964f3 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/integration_debugger.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/integration_debugger.tsx
@@ -39,7 +39,7 @@ import { queryClient } from '..';
import { pkgKeyFromPackageInfo } from '../../../services';
const fetchInstalledIntegrations = async () => {
- const response = await sendGetPackages({ experimental: true });
+ const response = await sendGetPackages({ prerelease: true });
if (response.error) {
throw new Error(response.error.message);
diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/index.ts b/x-pack/plugins/fleet/public/applications/integrations/hooks/index.ts
index 5b6b19af169f0..76b6b49c8c5cb 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/hooks/index.ts
+++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/index.ts
@@ -14,3 +14,5 @@ export * from './use_agent_policy_context';
export * from './use_integrations_state';
export * from './use_confirm_force_install';
export * from './use_confirm_open_unverified';
+export * from './use_packages';
+export * from './use_categories';
diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_categories.tsx b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_categories.tsx
new file mode 100644
index 0000000000000..8b441a229e9b7
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_categories.tsx
@@ -0,0 +1,55 @@
+/*
+ * 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 { useEffect, useCallback, useState } from 'react';
+
+import type { RequestError } from '../../fleet/hooks';
+import { sendGetCategories } from '../../fleet/hooks';
+import type { GetCategoriesResponse } from '../types';
+
+export function useCategories(prerelease?: boolean) {
+ const [data, setData] = useState();
+ const [error, setError] = useState();
+ const [isLoading, setIsLoading] = useState(false);
+ const [isPrereleaseEnabled, setIsPrereleaseEnabled] = useState(prerelease);
+
+ const fetchData = useCallback(async () => {
+ if (prerelease === undefined) {
+ return;
+ }
+ if (isPrereleaseEnabled === prerelease) {
+ return;
+ }
+ setIsPrereleaseEnabled(prerelease);
+ setIsLoading(true);
+ try {
+ const res = await sendGetCategories({
+ include_policy_templates: true,
+ prerelease,
+ });
+ if (res.error) {
+ throw res.error;
+ }
+ if (res.data) {
+ setData(res.data);
+ }
+ } catch (err) {
+ setError(err);
+ }
+ setIsLoading(false);
+ }, [prerelease, isPrereleaseEnabled]);
+
+ useEffect(() => {
+ fetchData();
+ }, [fetchData]);
+
+ return {
+ data,
+ error,
+ isLoading,
+ };
+}
diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_packages.tsx b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_packages.tsx
new file mode 100644
index 0000000000000..efb2f96ed57f3
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_packages.tsx
@@ -0,0 +1,56 @@
+/*
+ * 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 { useEffect, useCallback, useState } from 'react';
+
+import type { RequestError } from '../../fleet/hooks';
+import { sendGetPackages } from '../../fleet/hooks';
+import type { GetPackagesResponse } from '../types';
+
+export function usePackages(prerelease?: boolean) {
+ const [data, setData] = useState();
+ const [error, setError] = useState();
+ const [isLoading, setIsLoading] = useState(false);
+ const [isPrereleaseEnabled, setIsPrereleaseEnabled] = useState(prerelease);
+
+ const fetchData = useCallback(async () => {
+ if (prerelease === undefined) {
+ return;
+ }
+ if (isPrereleaseEnabled === prerelease) {
+ return;
+ }
+ setIsPrereleaseEnabled(prerelease);
+ setIsLoading(true);
+ try {
+ const res = await sendGetPackages({
+ category: '',
+ excludeInstallStatus: true,
+ prerelease,
+ });
+ if (res.error) {
+ throw res.error;
+ }
+ if (res.data) {
+ setData(res.data);
+ }
+ } catch (err) {
+ setError(err);
+ }
+ setIsLoading(false);
+ }, [prerelease, isPrereleaseEnabled]);
+
+ useEffect(() => {
+ fetchData();
+ }, [fetchData]);
+
+ return {
+ data,
+ error,
+ isLoading,
+ };
+}
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.stories.tsx
index 86b34f2415e2e..ebc18d84487db 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.stories.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.stories.tsx
@@ -32,5 +32,11 @@ export default {
} as Meta;
export const IntegrationPreference = () => {
- return ;
+ return (
+ {}}
+ />
+ );
};
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx
index b99683adbf8f4..4e4aafa271b3b 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React from 'react';
+import React, { useCallback, useEffect } from 'react';
import styled from 'styled-components';
import { i18n } from '@kbn/i18n';
@@ -20,9 +20,10 @@ import {
EuiIconTip,
EuiFlexGroup,
EuiFlexItem,
+ EuiSwitch,
} from '@elastic/eui';
-import { useStartServices } from '../../../hooks';
+import { sendPutSettings, useGetSettings, useStartServices } from '../../../hooks';
export type IntegrationPreferenceType = 'recommended' | 'beats' | 'agent';
@@ -34,6 +35,7 @@ interface Option {
export interface Props {
initialType: IntegrationPreferenceType;
onChange: (type: IntegrationPreferenceType) => void;
+ onPrereleaseEnabledChange: (prerelease: boolean) => void;
}
const recommendedTooltip = (
@@ -47,6 +49,10 @@ const Item = styled(EuiFlexItem)`
padding-left: ${(props) => props.theme.eui.euiSizeXS};
`;
+const EuiSwitchNoWrap = styled(EuiSwitch)`
+ white-space: nowrap;
+`;
+
const options: Option[] = [
{
type: 'recommended',
@@ -77,11 +83,46 @@ const options: Option[] = [
},
];
-export const IntegrationPreference = ({ initialType, onChange }: Props) => {
+export const IntegrationPreference = ({
+ initialType,
+ onChange,
+ onPrereleaseEnabledChange,
+}: Props) => {
const [idSelected, setIdSelected] = React.useState(initialType);
const { docLinks } = useStartServices();
+ const [prereleaseIntegrationsEnabled, setPrereleaseIntegrationsEnabled] = React.useState<
+ boolean | undefined
+ >(undefined);
+
+ const { data: settings, error: settingsError } = useGetSettings();
+
+ useEffect(() => {
+ const isEnabled = Boolean(settings?.item.prerelease_integrations_enabled);
+ if (settings?.item) {
+ setPrereleaseIntegrationsEnabled(isEnabled);
+ } else if (settingsError) {
+ setPrereleaseIntegrationsEnabled(false);
+ }
+ }, [settings?.item, settingsError]);
+
+ useEffect(() => {
+ if (prereleaseIntegrationsEnabled !== undefined) {
+ onPrereleaseEnabledChange(prereleaseIntegrationsEnabled);
+ }
+ }, [onPrereleaseEnabledChange, prereleaseIntegrationsEnabled]);
+
+ const updateSettings = useCallback(async (prerelease: boolean) => {
+ const res = await sendPutSettings({
+ prerelease_integrations_enabled: prerelease,
+ });
+
+ if (res.error) {
+ throw res.error;
+ }
+ }, []);
+
const link = (
{
label: option.label,
}));
+ const onPrereleaseSwitchChange = (
+ event: React.BaseSyntheticEvent<
+ React.MouseEvent,
+ HTMLButtonElement,
+ EventTarget & { checked: boolean }
+ >
+ ) => {
+ const isChecked = event.target.checked;
+ setPrereleaseIntegrationsEnabled(isChecked);
+ updateSettings(isChecked);
+ };
+
return (
+ {prereleaseIntegrationsEnabled !== undefined && (
+
+ )}
+
{title}
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx
index 49a8cbeb37d21..cb03f5321e578 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx
@@ -17,6 +17,7 @@ import type {
GetInfoResponse,
GetPackagePoliciesResponse,
GetStatsResponse,
+ GetSettingsResponse,
} from '../../../../../../../common/types/rest_spec';
import type {
DetailViewPanelName,
@@ -79,11 +80,23 @@ describe('when on integration detail', () => {
});
});
- describe('and the package is not installed', () => {
+ function mockGAAndPrereleaseVersions(pkgVersion: string) {
+ const unInstalledPackage = mockedApi.responseProvider.epmGetInfo('nginx');
+ unInstalledPackage.item.status = 'not_installed';
+ unInstalledPackage.item.version = pkgVersion;
+ mockedApi.responseProvider.epmGetInfo.mockImplementation((name, version, query) => {
+ if (query?.prerelease === false) {
+ const gaPackage = { item: { ...unInstalledPackage.item } };
+ gaPackage.item.version = '1.0.0';
+ return gaPackage;
+ }
+ return unInstalledPackage;
+ });
+ }
+
+ describe('and the package is not installed and prerelease enabled', () => {
beforeEach(async () => {
- const unInstalledPackage = mockedApi.responseProvider.epmGetInfo();
- unInstalledPackage.item.status = 'not_installed';
- mockedApi.responseProvider.epmGetInfo.mockReturnValue(unInstalledPackage);
+ mockGAAndPrereleaseVersions('1.0.0-beta');
await render();
});
@@ -96,6 +109,39 @@ describe('when on integration detail', () => {
await mockedApi.waitForApi();
expect(renderResult.queryByTestId('tab-policies')).toBeNull();
});
+
+ it('should display version select if prerelease setting enabled and prererelase version available', async () => {
+ await mockedApi.waitForApi();
+ const versionSelect = renderResult.queryByTestId('versionSelect');
+ expect(versionSelect?.textContent).toEqual('1.0.0-beta1.0.0');
+ expect((versionSelect as any)?.value).toEqual('1.0.0-beta');
+ });
+
+ it('should display prerelease callout if prerelease setting enabled and prerelease version available', async () => {
+ await mockedApi.waitForApi();
+ const calloutTitle = renderResult.getByTestId('prereleaseCallout');
+ expect(calloutTitle).toBeInTheDocument();
+ const calloutGABtn = renderResult.getByTestId('switchToGABtn');
+ expect((calloutGABtn as any)?.href).toEqual(
+ 'http://localhost/mock/app/integrations/detail/nginx-1.0.0/overview'
+ );
+ });
+ });
+
+ describe('and the package is not installed and prerelease disabled', () => {
+ beforeEach(async () => {
+ mockGAAndPrereleaseVersions('1.0.0');
+ mockedApi.responseProvider.getSettings.mockReturnValue({
+ item: { prerelease_integrations_enabled: false, id: '', fleet_server_hosts: [] },
+ });
+ await render();
+ });
+
+ it('should display version text and no callout if prerelease setting disabled', async () => {
+ await mockedApi.waitForApi();
+ expect((renderResult.queryByTestId('versionText') as any)?.textContent).toEqual('1.0.0');
+ expect(renderResult.queryByTestId('prereleaseCallout')).toBeNull();
+ });
});
describe('and a custom UI extension is NOT registered', () => {
@@ -267,13 +313,16 @@ interface MockedApi<
}
interface EpmPackageDetailsResponseProvidersMock {
- epmGetInfo: jest.MockedFunction<() => GetInfoResponse>;
+ epmGetInfo: jest.MockedFunction<
+ (pkgName: string, pkgVersion?: string, options?: { prerelease?: boolean }) => GetInfoResponse
+ >;
epmGetFile: jest.MockedFunction<() => string>;
epmGetStats: jest.MockedFunction<() => GetStatsResponse>;
fleetSetup: jest.MockedFunction<() => GetFleetStatusResponse>;
packagePolicyList: jest.MockedFunction<() => GetPackagePoliciesResponse>;
agentPolicyList: jest.MockedFunction<() => GetAgentPoliciesResponse>;
appCheckPermissions: jest.MockedFunction<() => CheckPermissionsResponse>;
+ getSettings: jest.MockedFunction<() => GetSettingsResponse>;
}
const mockApiCalls = (
@@ -753,6 +802,8 @@ On Windows, the module was tested with Nginx installed from the Chocolatey repos
success: true,
};
+ const getSettingsResponse = { item: { prerelease_integrations_enabled: true } };
+
const mockedApiInterface: MockedApi = {
waitForApi() {
return new Promise((resolve) => {
@@ -771,14 +822,19 @@ On Windows, the module was tested with Nginx installed from the Chocolatey repos
packagePolicyList: jest.fn().mockReturnValue(packagePoliciesResponse),
agentPolicyList: jest.fn().mockReturnValue(agentPoliciesResponse),
appCheckPermissions: jest.fn().mockReturnValue(appCheckPermissionsResponse),
+ getSettings: jest.fn().mockReturnValue(getSettingsResponse),
},
};
- http.get.mockImplementation(async (path: any) => {
+ http.get.mockImplementation((async (path: any, options: any) => {
if (typeof path === 'string') {
if (path === epmRouteService.getInfoPath(`nginx`, `0.3.7`)) {
markApiCallAsHandled();
- return mockedApiInterface.responseProvider.epmGetInfo();
+ return mockedApiInterface.responseProvider.epmGetInfo('nginx');
+ }
+ if (path === epmRouteService.getInfoPath(`nginx`)) {
+ markApiCallAsHandled();
+ return mockedApiInterface.responseProvider.epmGetInfo('nginx', undefined, options.query);
}
if (path === epmRouteService.getFilePath('/package/nginx/0.3.7/docs/README.md')) {
@@ -820,13 +876,16 @@ On Windows, the module was tested with Nginx installed from the Chocolatey repos
if (path === '/api/fleet/agents') {
return Promise.resolve();
}
+ if (path === '/api/fleet/settings') {
+ return mockedApiInterface.responseProvider.getSettings();
+ }
const err = new Error(`API [GET ${path}] is not MOCKED!`);
// eslint-disable-next-line no-console
console.error(err);
throw err;
}
- });
+ }) as any);
return mockedApiInterface;
};
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx
index d649bede3db44..42c5cc535d6b7 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx
@@ -17,6 +17,7 @@ import {
EuiDescriptionListTitle,
EuiFlexGroup,
EuiFlexItem,
+ EuiSelect,
EuiSpacer,
EuiText,
} from '@elastic/eui';
@@ -36,9 +37,10 @@ import {
useAuthz,
usePermissionCheck,
useIntegrationsStateContext,
+ useGetSettings,
} from '../../../../hooks';
import { INTEGRATIONS_ROUTING_PATHS } from '../../../../constants';
-import { ExperimentalFeaturesService } from '../../../../services';
+import { ExperimentalFeaturesService, isPackagePrerelease } from '../../../../services';
import {
useGetPackageInfoByKey,
useLink,
@@ -107,7 +109,7 @@ export function Detail() {
const { getId: getAgentPolicyId } = useAgentPolicyContext();
const { getFromIntegrations } = useIntegrationsStateContext();
const { pkgkey, panel } = useParams();
- const { getHref } = useLink();
+ const { getHref, getPath } = useLink();
const canInstallPackages = useAuthz().integrations.installPackages;
const canReadPackageSettings = useAuthz().integrations.readPackageSettings;
const canReadIntegrationPolicies = useAuthz().integrations.readIntegrationPolicies;
@@ -153,6 +155,17 @@ export function Detail() {
packageInfo.savedObject &&
semverLt(packageInfo.savedObject.attributes.version, packageInfo.latestVersion);
+ const [prereleaseIntegrationsEnabled, setPrereleaseIntegrationsEnabled] = React.useState<
+ boolean | undefined
+ >();
+
+ const { data: settings } = useGetSettings();
+
+ useEffect(() => {
+ const isEnabled = Boolean(settings?.item.prerelease_integrations_enabled);
+ setPrereleaseIntegrationsEnabled(isEnabled);
+ }, [settings?.item.prerelease_integrations_enabled]);
+
const { pkgName, pkgVersion } = splitPkgKey(pkgkey);
// Fetch package info
const {
@@ -161,7 +174,34 @@ export function Detail() {
isLoading: packageInfoLoading,
isInitialRequest: packageIsInitialRequest,
resendRequest: refreshPackageInfo,
- } = useGetPackageInfoByKey(pkgName, pkgVersion);
+ } = useGetPackageInfoByKey(pkgName, pkgVersion, {
+ prerelease: prereleaseIntegrationsEnabled,
+ });
+
+ const [latestGAVersion, setLatestGAVersion] = useState();
+ const [latestPrereleaseVersion, setLatestPrereleaseVersion] = useState();
+
+ // fetch latest GA version (prerelease=false)
+ const { data: packageInfoLatestGAData } = useGetPackageInfoByKey(pkgName, '', {
+ prerelease: false,
+ });
+
+ useEffect(() => {
+ const pkg = packageInfoLatestGAData?.item;
+ const isGAVersion = pkg && !isPackagePrerelease(pkg.version);
+ if (isGAVersion) {
+ setLatestGAVersion(pkg.version);
+ }
+ }, [packageInfoLatestGAData?.item]);
+
+ // fetch latest Prerelease version (prerelease=true)
+ const { data: packageInfoLatestPrereleaseData } = useGetPackageInfoByKey(pkgName, '', {
+ prerelease: true,
+ });
+
+ useEffect(() => {
+ setLatestPrereleaseVersion(packageInfoLatestPrereleaseData?.item.version);
+ }, [packageInfoLatestPrereleaseData?.item.version]);
const { isFirstTimeAgentUser = false, isLoading: firstTimeUserLoading } =
useIsFirstTimeAgentUser();
@@ -326,6 +366,46 @@ export function Detail() {
]
);
+ const showVersionSelect = useMemo(
+ () =>
+ prereleaseIntegrationsEnabled &&
+ latestGAVersion &&
+ latestPrereleaseVersion &&
+ latestGAVersion !== latestPrereleaseVersion &&
+ (!packageInfo?.version ||
+ packageInfo.version === latestGAVersion ||
+ packageInfo.version === latestPrereleaseVersion),
+ [prereleaseIntegrationsEnabled, latestGAVersion, latestPrereleaseVersion, packageInfo?.version]
+ );
+
+ const versionOptions = useMemo(
+ () => [
+ {
+ value: latestPrereleaseVersion,
+ text: latestPrereleaseVersion,
+ },
+ {
+ value: latestGAVersion,
+ text: latestGAVersion,
+ },
+ ],
+ [latestPrereleaseVersion, latestGAVersion]
+ );
+
+ const versionLabel = i18n.translate('xpack.fleet.epm.versionLabel', {
+ defaultMessage: 'Version',
+ });
+
+ const onVersionChange = useCallback(
+ (version: string, packageName: string) => {
+ const path = getPath('integration_details_overview', {
+ pkgkey: `${packageName}-${version}`,
+ });
+ history.push(path);
+ },
+ [getPath, history]
+ );
+
const headerRightContent = useMemo(
() =>
packageInfo ? (
@@ -334,12 +414,24 @@ export function Detail() {
{[
{
- label: i18n.translate('xpack.fleet.epm.versionLabel', {
- defaultMessage: 'Version',
- }),
+ label: showVersionSelect ? undefined : versionLabel,
content: (
- {packageInfo.version}
+
+ {showVersionSelect ? (
+
+ onVersionChange(event.target.value, packageInfo.name)
+ }
+ />
+ ) : (
+ {packageInfo.version}
+ )}
+
{updateAvailable ? (
@@ -416,6 +508,10 @@ export function Detail() {
missingSecurityConfiguration,
integrationInfo?.title,
handleAddIntegrationPolicyClick,
+ onVersionChange,
+ showVersionSelect,
+ versionLabel,
+ versionOptions,
]
);
@@ -605,7 +701,11 @@ export function Detail() {
) : (
-
+
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx
index 51991fb8aab33..21b3fd0f4f11c 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx
@@ -6,14 +6,14 @@
*/
import React, { memo, useMemo } from 'react';
import styled from 'styled-components';
-import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiLink } from '@elastic/eui';
+import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiLink, EuiButton } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { isIntegrationPolicyTemplate } from '../../../../../../../../common/services';
-import { useFleetStatus, useStartServices } from '../../../../../../../hooks';
-import { isPackageUnverified } from '../../../../../../../services';
+import { useFleetStatus, useLink, useStartServices } from '../../../../../../../hooks';
+import { isPackagePrerelease, isPackageUnverified } from '../../../../../../../services';
import type { PackageInfo, RegistryPolicyTemplate } from '../../../../../types';
import { Screenshots } from './screenshots';
@@ -23,6 +23,7 @@ import { Details } from './details';
interface Props {
packageInfo: PackageInfo;
integrationInfo?: RegistryPolicyTemplate;
+ latestGAVersion?: string;
}
const LeftColumn = styled(EuiFlexItem)`
@@ -66,48 +67,97 @@ const UnverifiedCallout: React.FC = () => {
);
};
-export const OverviewPage: React.FC = memo(({ packageInfo, integrationInfo }) => {
- const screenshots = useMemo(
- () => integrationInfo?.screenshots || packageInfo.screenshots || [],
- [integrationInfo, packageInfo.screenshots]
- );
- const { packageVerificationKeyId } = useFleetStatus();
- const isUnverified = isPackageUnverified(packageInfo, packageVerificationKeyId);
+const PrereleaseCallout: React.FC<{
+ packageName: string;
+ latestGAVersion?: string;
+ packageTitle: string;
+}> = ({ packageName, packageTitle, latestGAVersion }) => {
+ const { getHref } = useLink();
+ const overviewPathLatestGA = getHref('integration_details_overview', {
+ pkgkey: `${packageName}-${latestGAVersion}`,
+ });
+
return (
-
-
-
- {isUnverified && }
- {packageInfo.readme ? (
-
- ) : null}
-
-
-
- {screenshots.length ? (
-
-
+
+ {latestGAVersion && (
+
+
+
-
- ) : null}
-
-
-
-
-
-
+
+
+ )}
+
+
+ >
);
-});
+};
+
+export const OverviewPage: React.FC = memo(
+ ({ packageInfo, integrationInfo, latestGAVersion }) => {
+ const screenshots = useMemo(
+ () => integrationInfo?.screenshots || packageInfo.screenshots || [],
+ [integrationInfo, packageInfo.screenshots]
+ );
+ const { packageVerificationKeyId } = useFleetStatus();
+ const isUnverified = isPackageUnverified(packageInfo, packageVerificationKeyId);
+ const isPrerelease = isPackagePrerelease(packageInfo.version);
+ return (
+
+
+
+ {isUnverified && }
+ {isPrerelease && (
+
+ )}
+ {packageInfo.readme ? (
+
+ ) : null}
+
+
+
+ {screenshots.length ? (
+
+
+
+ ) : null}
+
+
+
+
+
+
+ );
+ }
+);
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx
index 7f56592cdc84b..a763d26e2821b 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx
@@ -20,19 +20,17 @@ import {
isIntegrationPolicyTemplate,
} from '../../../../../../../common/services';
-import { useStartServices } from '../../../../hooks';
+import { useCategories, usePackages, useStartServices } from '../../../../hooks';
import { pagePathGetters } from '../../../../constants';
import {
- useGetCategories,
- useGetPackages,
useBreadcrumbs,
useGetAppendCustomIntegrations,
useGetReplacementCustomIntegrations,
useLink,
} from '../../../../hooks';
import { doesPackageHaveIntegrations } from '../../../../services';
-import type { GetPackagesResponse, PackageList } from '../../../../types';
+import type { PackageList } from '../../../../types';
import { PackageListGrid } from '../../components/package_list_grid';
import type { PackageListItem } from '../../../../types';
@@ -183,11 +181,11 @@ const packageListToIntegrationsList = (packages: PackageList): PackageList => {
// TODO: clintandrewhall - this component is hard to test due to the hooks, particularly those that use `http`
// or `location` to load data. Ideally, we'll split this into "connected" and "pure" components.
-export const AvailablePackages: React.FC<{
- allPackages?: GetPackagesResponse | null;
- isLoading: boolean;
-}> = ({ allPackages, isLoading }) => {
+export const AvailablePackages: React.FC<{}> = ({}) => {
const [preference, setPreference] = useState('recommended');
+ const [prereleaseIntegrationsEnabled, setPrereleaseIntegrationsEnabled] = React.useState<
+ boolean | undefined
+ >(undefined);
useBreadcrumbs('integrations_all');
@@ -222,10 +220,7 @@ export const AvailablePackages: React.FC<{
data: eprPackages,
isLoading: isLoadingAllPackages,
error: eprPackageLoadingError,
- } = useGetPackages({
- category: '',
- excludeInstallStatus: true,
- });
+ } = usePackages(prereleaseIntegrationsEnabled);
// Remove Kubernetes package granularity
if (eprPackages?.items) {
@@ -276,9 +271,7 @@ export const AvailablePackages: React.FC<{
data: eprCategories,
isLoading: isLoadingCategories,
error: eprCategoryLoadingError,
- } = useGetCategories({
- include_policy_templates: true,
- });
+ } = useCategories(prereleaseIntegrationsEnabled);
const categories: CategoryFacet[] = useMemo(() => {
const eprAndCustomCategories: CategoryFacet[] = isLoadingCategories
@@ -306,7 +299,13 @@ export const AvailablePackages: React.FC<{
let controls = [
-
+ {
+ setPrereleaseIntegrationsEnabled(isEnabled);
+ }}
+ />
,
];
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx
index 18d96fbd66346..5b9227553c79e 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx
@@ -15,7 +15,7 @@ import { installationStatuses } from '../../../../../../../common/constants';
import type { DynamicPage, DynamicPagePathValues, StaticPage } from '../../../../constants';
import { INTEGRATIONS_ROUTING_PATHS, INTEGRATIONS_SEARCH_QUERYPARAM } from '../../../../constants';
import { DefaultLayout } from '../../../../layouts';
-import { isPackageUnverified } from '../../../../services';
+import { isPackagePrerelease, isPackageUnverified } from '../../../../services';
import type { PackageListItem } from '../../../../types';
@@ -65,19 +65,24 @@ export const mapToCard = ({
let isUnverified = false;
+ let version = 'version' in item ? item.version || '' : '';
+
if (item.type === 'ui_link') {
uiInternalPathUrl = item.id.includes('language_client.')
? addBasePath(item.uiInternalPath)
: item.uiExternalLink || getAbsolutePath(item.uiInternalPath);
} else {
- let urlVersion = item.version;
- if ('savedObject' in item) {
- urlVersion = item.savedObject.attributes.version || item.version;
+ // installed package
+ if (
+ ['updates_available', 'installed'].includes(selectedCategory ?? '') &&
+ 'savedObject' in item
+ ) {
+ version = item.savedObject.attributes.version || item.version;
isUnverified = isPackageUnverified(item, packageVerificationKeyId);
}
const url = getHref('integration_details_overview', {
- pkgkey: `${item.name}-${urlVersion}`,
+ pkgkey: `${item.name}-${version}`,
...(item.integration ? { integration: item.integration } : {}),
});
@@ -90,6 +95,9 @@ export const mapToCard = ({
} else if ((item as CustomIntegration).isBeta === true) {
release = 'beta';
}
+ if (!isPackagePrerelease(version)) {
+ release = 'ga';
+ }
return {
id: `${item.type === 'ui_link' ? 'ui_link' : 'epr'}:${item.id}`,
@@ -100,7 +108,7 @@ export const mapToCard = ({
fromIntegrations: selectedCategory,
integration: 'integration' in item ? item.integration || '' : '',
name: 'name' in item ? item.name : item.id,
- version: 'version' in item ? item.version || '' : '',
+ version,
release,
categories: ((item.categories || []) as string[]).filter((c: string) => !!c),
isUnverified,
@@ -108,8 +116,9 @@ export const mapToCard = ({
};
export const EPMHomePage: React.FC = () => {
+ // loading packages to find installed ones
const { data: allPackages, isLoading } = useGetPackages({
- experimental: true,
+ prerelease: true,
});
const installedPackages = useMemo(
@@ -132,7 +141,7 @@ export const EPMHomePage: React.FC = () => {
-
+
diff --git a/x-pack/plugins/fleet/public/hooks/use_package_installations.tsx b/x-pack/plugins/fleet/public/hooks/use_package_installations.tsx
index 5a0b6285c71db..e4dabcff4e8a0 100644
--- a/x-pack/plugins/fleet/public/hooks/use_package_installations.tsx
+++ b/x-pack/plugins/fleet/public/hooks/use_package_installations.tsx
@@ -28,7 +28,7 @@ interface UpdatableIntegration {
export const usePackageInstallations = () => {
const { data: allPackages, isLoading: isLoadingPackages } = useGetPackages({
- experimental: true,
+ prerelease: true,
});
const { data: agentPolicyData, isLoading: isLoadingPolicies } = useGetAgentPolicies({
diff --git a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts
index 3a0033435ed9d..9c88cfae46c4d 100644
--- a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts
+++ b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts
@@ -44,7 +44,15 @@ export const useGetCategories = (query: GetCategoriesRequest['query'] = {}) => {
return useRequest({
path: epmRouteService.getCategoriesPath(),
method: 'get',
- query: { experimental: true, ...query },
+ query,
+ });
+};
+
+export const sendGetCategories = (query: GetCategoriesRequest['query'] = {}) => {
+ return sendRequest({
+ path: epmRouteService.getCategoriesPath(),
+ method: 'get',
+ query,
});
};
@@ -52,7 +60,7 @@ export const useGetPackages = (query: GetPackagesRequest['query'] = {}) => {
return useRequest({
path: epmRouteService.getListPath(),
method: 'get',
- query: { experimental: true, ...query },
+ query,
});
};
@@ -60,7 +68,7 @@ export const sendGetPackages = (query: GetPackagesRequest['query'] = {}) => {
return sendRequest({
path: epmRouteService.getListPath(),
method: 'get',
- query: { experimental: true, ...query },
+ query,
});
};
@@ -74,14 +82,22 @@ export const useGetLimitedPackages = () => {
export const useGetPackageInfoByKey = (
pkgName: string,
pkgVersion?: string,
- ignoreUnverified: boolean = false
+ options?: {
+ ignoreUnverified?: boolean;
+ prerelease?: boolean;
+ }
) => {
const confirmOpenUnverified = useConfirmOpenUnverified();
- const [ignoreUnverifiedQueryParam, setIgnoreUnverifiedQueryParam] = useState(ignoreUnverified);
+ const [ignoreUnverifiedQueryParam, setIgnoreUnverifiedQueryParam] = useState(
+ options?.ignoreUnverified
+ );
const res = useRequest({
path: epmRouteService.getInfoPath(pkgName, pkgVersion),
method: 'get',
- query: ignoreUnverifiedQueryParam ? { ignoreUnverified: ignoreUnverifiedQueryParam } : {},
+ query: {
+ ...options,
+ ...(ignoreUnverifiedQueryParam ? { ignoreUnverified: ignoreUnverifiedQueryParam } : {}),
+ },
});
useEffect(() => {
@@ -111,12 +127,15 @@ export const useGetPackageStats = (pkgName: string) => {
export const sendGetPackageInfoByKey = (
pkgName: string,
pkgVersion?: string,
- ignoreUnverified?: boolean
+ options?: {
+ ignoreUnverified?: boolean;
+ prerelease?: boolean;
+ }
) => {
return sendRequest({
path: epmRouteService.getInfoPath(pkgName, pkgVersion),
method: 'get',
- query: ignoreUnverified ? { ignoreUnverified } : {},
+ query: options,
});
};
diff --git a/x-pack/plugins/fleet/public/services/index.ts b/x-pack/plugins/fleet/public/services/index.ts
index d6167b4548e65..9d6ef9b4563ae 100644
--- a/x-pack/plugins/fleet/public/services/index.ts
+++ b/x-pack/plugins/fleet/public/services/index.ts
@@ -47,3 +47,4 @@ export { pkgKeyFromPackageInfo } from './pkg_key_from_package_info';
export { createExtensionRegistrationCallback } from './ui_extensions';
export { incrementPolicyName } from './increment_policy_name';
export { policyHasFleetServer } from './has_fleet_server';
+export { isPackagePrerelease } from './package_prerelease';
diff --git a/x-pack/plugins/fleet/public/services/package_prerelease.test.ts b/x-pack/plugins/fleet/public/services/package_prerelease.test.ts
new file mode 100644
index 0000000000000..c8843d304de55
--- /dev/null
+++ b/x-pack/plugins/fleet/public/services/package_prerelease.test.ts
@@ -0,0 +1,34 @@
+/*
+ * 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 { isPackagePrerelease } from './package_prerelease';
+
+describe('isPackagePrerelease', () => {
+ it('should return prerelease true for 0.1.0', () => {
+ expect(isPackagePrerelease('0.1.0')).toBe(true);
+ });
+
+ it('should return prerelease false for 1.1.0', () => {
+ expect(isPackagePrerelease('1.1.0')).toBe(false);
+ });
+
+ it('should return prerelease true for 1.0.0-preview', () => {
+ expect(isPackagePrerelease('1.0.0-preview')).toBe(true);
+ });
+
+ it('should return prerelease true for 1.0.0-beta', () => {
+ expect(isPackagePrerelease('1.0.0-beta')).toBe(true);
+ });
+
+ it('should return prerelease true for 1.0.0-rc', () => {
+ expect(isPackagePrerelease('1.0.0-rc')).toBe(true);
+ });
+
+ it('should return prerelease true for 1.0.0-dev.0', () => {
+ expect(isPackagePrerelease('1.0.0-dev.0')).toBe(true);
+ });
+});
diff --git a/x-pack/plugins/fleet/public/services/package_prerelease.ts b/x-pack/plugins/fleet/public/services/package_prerelease.ts
new file mode 100644
index 0000000000000..0df6e0deee409
--- /dev/null
+++ b/x-pack/plugins/fleet/public/services/package_prerelease.ts
@@ -0,0 +1,11 @@
+/*
+ * 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.
+ */
+
+export function isPackagePrerelease(version: string): boolean {
+ // derive from semver
+ return version.startsWith('0') || version.includes('-');
+}
diff --git a/x-pack/plugins/fleet/scripts/install_all_packages/install_all_packages.ts b/x-pack/plugins/fleet/scripts/install_all_packages/install_all_packages.ts
index e07ff9f5a1808..4215a460a29cb 100644
--- a/x-pack/plugins/fleet/scripts/install_all_packages/install_all_packages.ts
+++ b/x-pack/plugins/fleet/scripts/install_all_packages/install_all_packages.ts
@@ -57,7 +57,7 @@ async function deletePackage(name: string, version: string) {
async function getAllPackages() {
const res = await fetch(
- `${REGISTRY_URL}/search?experimental=true&kibana.version=${KIBANA_VERSION}`,
+ `${REGISTRY_URL}/search?prerelease=true&kibana.version=${KIBANA_VERSION}`,
{
headers: {
accept: '*/*',
diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts
index 5d177641daee5..ee69ae9fc8e65 100644
--- a/x-pack/plugins/fleet/server/plugin.ts
+++ b/x-pack/plugins/fleet/server/plugin.ts
@@ -572,7 +572,7 @@ export class FleetPlugin
internalSoClient,
this.getLogger()
);
- return this.packageService;
+ return this.packageService!;
}
private getLogger(): Logger {
diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts
index 242e272cd184b..5ef609fe9b6cc 100644
--- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts
+++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts
@@ -37,6 +37,7 @@ import type {
GetStatsRequestSchema,
FleetRequestHandler,
UpdatePackageRequestSchema,
+ GetLimitedPackagesRequestSchema,
} from '../../types';
import {
bulkInstallPackages,
@@ -67,7 +68,9 @@ export const getCategoriesHandler: FleetRequestHandler<
TypeOf
> = async (context, request, response) => {
try {
- const res = await getCategories(request.query);
+ const res = await getCategories({
+ ...request.query,
+ });
const body: GetCategoriesResponse = {
items: res,
response: res,
@@ -103,10 +106,17 @@ export const getListHandler: FleetRequestHandler<
}
};
-export const getLimitedListHandler: FleetRequestHandler = async (context, request, response) => {
+export const getLimitedListHandler: FleetRequestHandler<
+ undefined,
+ TypeOf,
+ undefined
+> = async (context, request, response) => {
try {
const savedObjectsClient = (await context.fleet).epm.internalSoClient;
- const res = await getLimitedPackages({ savedObjectsClient });
+ const res = await getLimitedPackages({
+ savedObjectsClient,
+ prerelease: request.query.prerelease,
+ });
const body: GetLimitedPackagesResponse = {
items: res,
response: res,
@@ -200,7 +210,7 @@ export const getInfoHandler: FleetRequestHandler<
try {
const savedObjectsClient = (await context.fleet).epm.internalSoClient;
const { pkgName, pkgVersion } = request.params;
- const { ignoreUnverified = false } = request.query;
+ const { ignoreUnverified = false, prerelease } = request.query;
if (pkgVersion && !semverValid(pkgVersion)) {
throw new FleetError('Package version is not a valid semver');
}
@@ -210,6 +220,7 @@ export const getInfoHandler: FleetRequestHandler<
pkgVersion: pkgVersion || '',
skipArchive: true,
ignoreUnverified,
+ prerelease,
});
const body: GetInfoResponse = {
item: res,
@@ -307,7 +318,7 @@ const bulkInstallServiceResponseToHttpEntry = (
export const bulkInstallPackagesFromRegistryHandler: FleetRequestHandler<
undefined,
- undefined,
+ TypeOf,
TypeOf
> = async (context, request, response) => {
const coreContext = await context.core;
@@ -320,6 +331,7 @@ export const bulkInstallPackagesFromRegistryHandler: FleetRequestHandler<
esClient,
packagesToInstall: request.body.packages,
spaceId,
+ prerelease: request.query.prerelease,
});
const payload = bulkInstalledResponses.map(bulkInstallServiceResponseToHttpEntry);
const body: BulkInstallPackagesResponse = {
diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts
index d96d574f77bed..ee9caa4def673 100644
--- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts
+++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts
@@ -204,6 +204,7 @@ export const createPackagePolicyHandler: FleetRequestHandler<
pkgName: pkg.name,
pkgVersion: pkg.version,
ignoreUnverified: force,
+ prerelease: true,
});
newPackagePolicy = simplifiedPackagePolicytoNewPackagePolicy(newPolicy, pkgInfo, {
experimental_data_stream_features: pkg.experimental_data_stream_features,
diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts
index 4a943789cac68..5721d2e6b7ed6 100644
--- a/x-pack/plugins/fleet/server/saved_objects/index.ts
+++ b/x-pack/plugins/fleet/server/saved_objects/index.ts
@@ -70,6 +70,7 @@ const getSavedObjectTypes = (
properties: {
fleet_server_hosts: { type: 'keyword' },
has_seen_add_data_notice: { type: 'boolean', index: false },
+ prerelease_integrations_enabled: { type: 'boolean' },
},
},
migrations: {
diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_6_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_6_0.ts
index 5134249ddd1ef..e43406d859600 100644
--- a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_6_0.ts
+++ b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_6_0.ts
@@ -16,5 +16,7 @@ export const migrateSettingsToV860: SavedObjectMigrationFn =
// @ts-expect-error has_seen_fleet_migration_notice property does not exists anymore
delete settingsDoc.attributes.has_seen_fleet_migration_notice;
+ settingsDoc.attributes.prerelease_integrations_enabled = false;
+
return settingsDoc;
};
diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.test.ts b/x-pack/plugins/fleet/server/services/epm/package_service.test.ts
index f4f853d778923..44d35f3e4c33c 100644
--- a/x-pack/plugins/fleet/server/services/epm/package_service.test.ts
+++ b/x-pack/plugins/fleet/server/services/epm/package_service.test.ts
@@ -92,7 +92,7 @@ function getTest(
method: mocks.packageClient.fetchFindLatestPackage.bind(mocks.packageClient),
args: ['package name'],
spy: jest.spyOn(epmRegistry, 'fetchFindLatestPackageOrThrow'),
- spyArgs: ['package name'],
+ spyArgs: ['package name', undefined],
spyResponse: { name: 'fetchFindLatestPackage test' },
expectedReturnValue: { name: 'fetchFindLatestPackage test' },
};
diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.ts b/x-pack/plugins/fleet/server/services/epm/package_service.ts
index 45fb673771327..f3d82f13d96ee 100644
--- a/x-pack/plugins/fleet/server/services/epm/package_service.ts
+++ b/x-pack/plugins/fleet/server/services/epm/package_service.ts
@@ -26,6 +26,7 @@ import { checkSuperuser } from '../../routes/security';
import { FleetUnauthorizedError } from '../../errors';
import { installTransforms, isTransform } from './elasticsearch/transform/install';
+import type { FetchFindLatestPackageOptions } from './registry';
import { fetchFindLatestPackageOrThrow, getPackage } from './registry';
import { ensureInstalledPackage, getInstallation } from './packages';
@@ -45,7 +46,10 @@ export interface PackageClient {
spaceId?: string;
}): Promise;
- fetchFindLatestPackage(packageName: string): Promise;
+ fetchFindLatestPackage(
+ packageName: string,
+ options?: FetchFindLatestPackageOptions
+ ): Promise;
getPackage(
packageName: string,
@@ -116,9 +120,12 @@ class PackageClientImpl implements PackageClient {
});
}
- public async fetchFindLatestPackage(packageName: string) {
+ public async fetchFindLatestPackage(
+ packageName: string,
+ options?: FetchFindLatestPackageOptions
+ ): Promise {
await this.#runPreflight();
- return fetchFindLatestPackageOrThrow(packageName);
+ return fetchFindLatestPackageOrThrow(packageName, options);
}
public async getPackage(
diff --git a/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts b/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts
index a9d027ce51c8a..66b9323dd0939 100644
--- a/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts
+++ b/x-pack/plugins/fleet/server/services/epm/packages/bulk_install_packages.ts
@@ -22,6 +22,7 @@ interface BulkInstallPackagesParams {
force?: boolean;
spaceId: string;
preferredSource?: 'registry' | 'bundled';
+ prerelease?: boolean;
}
export async function bulkInstallPackages({
@@ -30,6 +31,7 @@ export async function bulkInstallPackages({
esClient,
spaceId,
force,
+ prerelease,
}: BulkInstallPackagesParams): Promise {
const logger = appContextService.getLogger();
@@ -39,7 +41,7 @@ export async function bulkInstallPackages({
return Promise.resolve(pkg);
}
- return Registry.fetchFindLatestPackageOrThrow(pkg);
+ return Registry.fetchFindLatestPackageOrThrow(pkg, { prerelease });
})
);
diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts
index 20ed655d97176..19ced885822a4 100644
--- a/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts
+++ b/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts
@@ -22,10 +22,17 @@ import { appContextService } from '../../app_context';
import { PackageNotFoundError } from '../../../errors';
+import { getSettings } from '../../settings';
+
import { getPackageInfo, getPackageUsageStats } from './get';
const MockRegistry = Registry as jest.Mocked;
+jest.mock('../../settings');
+
+const mockGetSettings = getSettings as jest.Mock;
+mockGetSettings.mockResolvedValue({ prerelease_integrations_enabled: true });
+
describe('When using EPM `get` services', () => {
describe('and invoking getPackageUsageStats()', () => {
let soClient: jest.Mocked;
diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get.ts b/x-pack/plugins/fleet/server/services/epm/packages/get.ts
index b8b447a8de526..e8d1cd1380303 100644
--- a/x-pack/plugins/fleet/server/services/epm/packages/get.ts
+++ b/x-pack/plugins/fleet/server/services/epm/packages/get.ts
@@ -49,8 +49,14 @@ export async function getPackages(
excludeInstallStatus?: boolean;
} & Registry.SearchParams
) {
- const { savedObjectsClient, experimental, category, excludeInstallStatus = false } = options;
- const registryItems = await Registry.fetchList({ category, experimental }).then((items) => {
+ const {
+ savedObjectsClient,
+ category,
+ excludeInstallStatus = false,
+ prerelease = false,
+ } = options;
+
+ const registryItems = await Registry.fetchList({ category, prerelease }).then((items) => {
return items.map((item) =>
Object.assign({}, item, { title: item.title || nameAsTitle(item.name) }, { id: item.name })
);
@@ -87,11 +93,12 @@ export async function getPackages(
// Get package names for packages which cannot have more than one package policy on an agent policy
export async function getLimitedPackages(options: {
savedObjectsClient: SavedObjectsClientContract;
+ prerelease?: boolean;
}): Promise {
- const { savedObjectsClient } = options;
+ const { savedObjectsClient, prerelease } = options;
const allPackages = await getPackages({
savedObjectsClient,
- experimental: true,
+ prerelease,
});
const installedPackages = allPackages.filter(
(pkg) => pkg.status === installationStatuses.Installed
@@ -126,6 +133,7 @@ export async function getPackageInfo({
pkgVersion,
skipArchive = false,
ignoreUnverified = false,
+ prerelease,
}: {
savedObjectsClient: SavedObjectsClientContract;
pkgName: string;
@@ -133,10 +141,11 @@ export async function getPackageInfo({
/** Avoid loading the registry archive into the cache (only use for performance reasons). Defaults to `false` */
skipArchive?: boolean;
ignoreUnverified?: boolean;
+ prerelease?: boolean;
}): Promise {
const [savedObject, latestPackage] = await Promise.all([
getInstallationObject({ savedObjectsClient, pkgName }),
- Registry.fetchFindLatestPackageOrUndefined(pkgName),
+ Registry.fetchFindLatestPackageOrUndefined(pkgName, { prerelease }),
]);
if (!savedObject && !latestPackage) {
diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get_prerelease_setting.ts b/x-pack/plugins/fleet/server/services/epm/packages/get_prerelease_setting.ts
new file mode 100644
index 0000000000000..df4b47d13ef2f
--- /dev/null
+++ b/x-pack/plugins/fleet/server/services/epm/packages/get_prerelease_setting.ts
@@ -0,0 +1,25 @@
+/*
+ * 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 { SavedObjectsClientContract } from '@kbn/core/server';
+
+import { appContextService } from '../../app_context';
+import { getSettings } from '../../settings';
+
+export async function getPrereleaseFromSettings(
+ savedObjectsClient: SavedObjectsClientContract
+): Promise {
+ let prerelease: boolean = false;
+ try {
+ ({ prerelease_integrations_enabled: prerelease } = await getSettings(savedObjectsClient));
+ } catch (err) {
+ appContextService
+ .getLogger()
+ .warn('Error while trying to load prerelease flag from settings, defaulting to false', err);
+ }
+ return prerelease;
+}
diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts
index ebd0bde8b09b4..d7f67ca1d2ae0 100644
--- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts
+++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts
@@ -113,7 +113,7 @@ export async function ensureInstalledPackage(options: {
// If pkgVersion isn't specified, find the latest package version
const pkgKeyProps = pkgVersion
? { name: pkgName, version: pkgVersion }
- : await Registry.fetchFindLatestPackageOrThrow(pkgName);
+ : await Registry.fetchFindLatestPackageOrThrow(pkgName, { prerelease: true });
const installedPackageResult = await isPackageVersionOrLaterInstalled({
savedObjectsClient,
@@ -234,6 +234,7 @@ interface InstallRegistryPackageParams {
force?: boolean;
neverIgnoreVerificationError?: boolean;
ignoreConstraints?: boolean;
+ prerelease?: boolean;
}
interface InstallUploadedArchiveParams {
savedObjectsClient: SavedObjectsClientContract;
@@ -301,6 +302,7 @@ async function installPackageFromRegistry({
const [latestPackage, { paths, packageInfo, verificationResult }] = await Promise.all([
Registry.fetchFindLatestPackageOrThrow(pkgName, {
ignoreConstraints,
+ prerelease: true,
}),
Registry.getPackage(pkgName, pkgVersion, {
ignoreUnverified: force && !neverIgnoreVerificationError,
diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.ts
index 4213a50ebf5bf..a6259d1eb6552 100644
--- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts
+++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts
@@ -25,6 +25,7 @@ import type {
GetCategoriesRequest,
PackageVerificationResult,
ArchivePackage,
+ BundledPackage,
} from '../../../types';
import {
getArchiveFilelist,
@@ -55,6 +56,8 @@ import { getRegistryUrl } from './registry_url';
export interface SearchParams {
category?: CategoryId;
+ prerelease?: boolean;
+ // deprecated
experimental?: boolean;
}
@@ -70,8 +73,8 @@ export async function fetchList(params?: SearchParams): Promise {
return withPackageSpan(`Find latest package ${packageName}`, async () => {
const logger = appContextService.getLogger();
- const { ignoreConstraints = false } = options ?? {};
+ const { ignoreConstraints = false, prerelease = false } = options ?? {};
const bundledPackage = await getBundledPackageByName(packageName);
+ // temporary workaround to allow synthetics package beta version until there is a GA available
+ // needed because synthetics is installed by default on kibana startup
+ const prereleaseAllowedExceptions = ['synthetics'];
+
+ const prereleaseEnabled = prerelease || prereleaseAllowedExceptions.includes(packageName);
+
const registryUrl = getRegistryUrl();
- const url = new URL(`${registryUrl}/search?package=${packageName}&experimental=true`);
+ const url = new URL(
+ `${registryUrl}/search?package=${packageName}&prerelease=${prereleaseEnabled}`
+ );
if (!ignoreConstraints) {
setKibanaVersion(url);
@@ -224,8 +236,8 @@ export async function fetchCategories(
const registryUrl = getRegistryUrl();
const url = new URL(`${registryUrl}/categories`);
if (params) {
- if (params.experimental) {
- url.searchParams.set('experimental', params.experimental.toString());
+ if (params.prerelease) {
+ url.searchParams.set('prerelease', params.prerelease.toString());
}
if (params.include_policy_templates) {
url.searchParams.set('include_policy_templates', params.include_policy_templates.toString());
diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts
index f52316dd4452b..b4e78d2f4c2ca 100644
--- a/x-pack/plugins/fleet/server/services/package_policy.ts
+++ b/x-pack/plugins/fleet/server/services/package_policy.ts
@@ -187,6 +187,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient {
savedObjectsClient: soClient,
pkgName: packagePolicy.package.name,
pkgVersion: packagePolicy.package.version,
+ prerelease: true,
}));
// Check if it is a limited package, and if so, check that the corresponding agent policy does not
@@ -508,6 +509,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient {
savedObjectsClient: soClient,
pkgName: packagePolicy.package.name,
pkgVersion: packagePolicy.package.version,
+ prerelease: true,
});
validatePackagePolicyOrThrow(packagePolicy, pkgInfo);
@@ -801,6 +803,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient {
savedObjectsClient: soClient,
pkgName: packagePolicy!.package!.name,
pkgVersion: pkgVersion ?? '',
+ prerelease: !!pkgVersion, // using prerelease only if version is specified
});
}
@@ -1129,6 +1132,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient {
pkgName,
pkgVersion,
skipArchive: true,
+ prerelease: true,
});
if (packageInfo) {
return packageToPackagePolicy(packageInfo, '');
@@ -1146,6 +1150,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient {
savedObjectsClient: soClient,
pkgName: pkgInstall.name,
pkgVersion: pkgInstall.version,
+ prerelease: true,
});
if (packageInfo) {
@@ -1594,6 +1599,7 @@ async function getPackageInfoForPackagePolicies(
savedObjectsClient: soClient,
pkgName: pkgInfo.name,
pkgVersion: pkgInfo.version,
+ prerelease: true,
});
resultMap.set(pkgKey, pkgInfoData);
diff --git a/x-pack/plugins/fleet/server/services/settings.ts b/x-pack/plugins/fleet/server/services/settings.ts
index 5cde2dbf99815..e710251c39f91 100644
--- a/x-pack/plugins/fleet/server/services/settings.ts
+++ b/x-pack/plugins/fleet/server/services/settings.ts
@@ -99,5 +99,5 @@ function getConfigFleetServerHosts() {
}
export function createDefaultSettings(): BaseSettings {
- return {};
+ return { prerelease_integrations_enabled: false };
}
diff --git a/x-pack/plugins/fleet/server/types/rest_spec/epm.ts b/x-pack/plugins/fleet/server/types/rest_spec/epm.ts
index f69576a2a8b56..9e36413f80150 100644
--- a/x-pack/plugins/fleet/server/types/rest_spec/epm.ts
+++ b/x-pack/plugins/fleet/server/types/rest_spec/epm.ts
@@ -9,7 +9,8 @@ import { schema } from '@kbn/config-schema';
export const GetCategoriesRequestSchema = {
query: schema.object({
- experimental: schema.maybe(schema.boolean()),
+ prerelease: schema.maybe(schema.boolean()),
+ experimental: schema.maybe(schema.boolean()), // deprecated
include_policy_templates: schema.maybe(schema.boolean()),
}),
};
@@ -17,11 +18,18 @@ export const GetCategoriesRequestSchema = {
export const GetPackagesRequestSchema = {
query: schema.object({
category: schema.maybe(schema.string()),
- experimental: schema.maybe(schema.boolean()),
+ prerelease: schema.maybe(schema.boolean()),
+ experimental: schema.maybe(schema.boolean()), // deprecated
excludeInstallStatus: schema.maybe(schema.boolean({ defaultValue: false })),
}),
};
+export const GetLimitedPackagesRequestSchema = {
+ query: schema.object({
+ prerelease: schema.maybe(schema.boolean()),
+ }),
+};
+
export const GetFileRequestSchema = {
params: schema.object({
pkgName: schema.string(),
@@ -37,6 +45,7 @@ export const GetInfoRequestSchema = {
}),
query: schema.object({
ignoreUnverified: schema.maybe(schema.boolean()),
+ prerelease: schema.maybe(schema.boolean()),
}),
};
@@ -44,6 +53,10 @@ export const GetInfoRequestSchemaDeprecated = {
params: schema.object({
pkgkey: schema.string(),
}),
+ query: schema.object({
+ ignoreUnverified: schema.maybe(schema.boolean()),
+ prerelease: schema.maybe(schema.boolean()),
+ }),
};
export const UpdatePackageRequestSchema = {
@@ -96,6 +109,9 @@ export const InstallPackageFromRegistryRequestSchemaDeprecated = {
};
export const BulkUpgradePackagesFromRegistryRequestSchema = {
+ query: schema.object({
+ prerelease: schema.maybe(schema.boolean()),
+ }),
body: schema.object({
packages: schema.arrayOf(schema.string(), { minSize: 1 }),
}),
diff --git a/x-pack/plugins/fleet/server/types/rest_spec/settings.ts b/x-pack/plugins/fleet/server/types/rest_spec/settings.ts
index 4544b677cba8c..4a1c2975c80e0 100644
--- a/x-pack/plugins/fleet/server/types/rest_spec/settings.ts
+++ b/x-pack/plugins/fleet/server/types/rest_spec/settings.ts
@@ -35,5 +35,6 @@ export const PutSettingsRequestSchema = {
})
),
kibana_ca_sha256: schema.maybe(schema.string()),
+ prerelease_integrations_enabled: schema.maybe(schema.boolean()),
}),
};
diff --git a/x-pack/test/fleet_api_integration/apis/epm/bulk_upgrade.ts b/x-pack/test/fleet_api_integration/apis/epm/bulk_upgrade.ts
index 29110f5807edf..fe875fe4e2565 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/bulk_upgrade.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/bulk_upgrade.ts
@@ -62,7 +62,7 @@ export default function (providerContext: FtrProviderContext) {
});
it('should return 200 and an array for upgrading a package', async function () {
const { body }: { body: BulkInstallPackagesResponse } = await supertest
- .post(`/api/fleet/epm/packages/_bulk`)
+ .post(`/api/fleet/epm/packages/_bulk?prerelease=true`)
.set('kbn-xsrf', 'xxxx')
.send({ packages: ['multiple_versions'] })
.expect(200);
@@ -73,7 +73,7 @@ export default function (providerContext: FtrProviderContext) {
});
it('should return an error for packages that do not exist', async function () {
const { body }: { body: BulkInstallPackagesResponse } = await supertest
- .post(`/api/fleet/epm/packages/_bulk`)
+ .post(`/api/fleet/epm/packages/_bulk?prerelease=true`)
.set('kbn-xsrf', 'xxxx')
.send({ packages: ['multiple_versions', 'blahblah'] })
.expect(200);
@@ -88,7 +88,7 @@ export default function (providerContext: FtrProviderContext) {
});
it('should upgrade multiple packages', async function () {
const { body }: { body: BulkInstallPackagesResponse } = await supertest
- .post(`/api/fleet/epm/packages/_bulk`)
+ .post(`/api/fleet/epm/packages/_bulk?prerelease=true`)
.set('kbn-xsrf', 'xxxx')
.send({ packages: ['multiple_versions', 'overrides'] })
.expect(200);
@@ -110,7 +110,7 @@ export default function (providerContext: FtrProviderContext) {
it('should return 200 and an array for upgrading a package', async function () {
const { body }: { body: BulkInstallPackagesResponse } = await supertest
- .post(`/api/fleet/epm/packages/_bulk`)
+ .post(`/api/fleet/epm/packages/_bulk?prerelease=true`)
.set('kbn-xsrf', 'xxxx')
.send({ packages: ['multiple_versions'] })
.expect(200);
diff --git a/x-pack/test/fleet_api_integration/apis/epm/custom_ingest_pipeline.ts b/x-pack/test/fleet_api_integration/apis/epm/custom_ingest_pipeline.ts
index 0ef5f648ab36d..8d7a6323d5f73 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/custom_ingest_pipeline.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/custom_ingest_pipeline.ts
@@ -32,7 +32,7 @@ export default function (providerContext: FtrProviderContext) {
// Use the custom log package to test the custom ingest pipeline
before(async () => {
const { body: getPackagesRes } = await supertest.get(
- `/api/fleet/epm/packages?experimental=true`
+ `/api/fleet/epm/packages?prerelease=true`
);
const logPackage = getPackagesRes.items.find((p: any) => p.name === 'log');
if (!logPackage) {
diff --git a/x-pack/test/fleet_api_integration/apis/epm/delete.ts b/x-pack/test/fleet_api_integration/apis/epm/delete.ts
index 076cc6bba4efc..9ec8b7318f6c9 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/delete.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/delete.ts
@@ -34,6 +34,7 @@ export default function (providerContext: FtrProviderContext) {
describe('delete and force delete scenarios', async () => {
skipIfNoDockerRegistry(providerContext);
setupFleetAndAgents(providerContext);
+
before(async () => {
await installPackage(requiredPackage, pkgVersion);
});
diff --git a/x-pack/test/fleet_api_integration/apis/epm/final_pipeline.ts b/x-pack/test/fleet_api_integration/apis/epm/final_pipeline.ts
index e705aa1734cb0..5e677bd96d54a 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/final_pipeline.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/final_pipeline.ts
@@ -43,7 +43,7 @@ export default function (providerContext: FtrProviderContext) {
// Use the custom log package to test the fleet final pipeline
before(async () => {
const { body: getPackagesRes } = await supertest.get(
- `/api/fleet/epm/packages?experimental=true`
+ `/api/fleet/epm/packages?prerelease=true`
);
const logPackage = getPackagesRes.items.find((p: any) => p.name === 'log');
if (!logPackage) {
diff --git a/x-pack/test/fleet_api_integration/apis/epm/get.ts b/x-pack/test/fleet_api_integration/apis/epm/get.ts
index 280922b2e4a33..17f83b0b3c534 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/get.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/get.ts
@@ -40,6 +40,7 @@ export default function (providerContext: FtrProviderContext) {
describe('EPM - get', () => {
skipIfNoDockerRegistry(providerContext);
setupFleetAndAgents(providerContext);
+
it('returns package info from the registry if it was installed from the registry', async function () {
// this will install through the registry by default
await installPackage(testPkgName, testPkgVersion);
@@ -149,7 +150,7 @@ export default function (providerContext: FtrProviderContext) {
// not from the package registry. This is because they contain a field the registry
// does not support
const res = await supertest
- .get(`/api/fleet/epm/packages/integration_to_input/0.9.1`)
+ .get(`/api/fleet/epm/packages/integration_to_input/0.9.1?prerelease=true`)
.expect(200);
const packageInfo = res.body.item;
@@ -158,14 +159,16 @@ export default function (providerContext: FtrProviderContext) {
});
describe('Pkg verification', () => {
it('should return validation error for unverified input only pkg', async function () {
- const res = await supertest.get(`/api/fleet/epm/packages/input_only/0.1.0`).expect(400);
+ const res = await supertest
+ .get(`/api/fleet/epm/packages/input_only/0.1.0?prerelease=true`)
+ .expect(400);
const error = res.body;
expect(error?.attributes?.type).to.equal('verification_failed');
});
it('should not return validation error for unverified input only pkg if ignoreUnverified is true', async function () {
await supertest
- .get(`/api/fleet/epm/packages/input_only/0.1.0?ignoreUnverified=true`)
+ .get(`/api/fleet/epm/packages/input_only/0.1.0?ignoreUnverified=true&prerelease=true`)
.expect(200);
});
});
diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts b/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts
index e4fd8f11141ae..d7c913fc8bc4b 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts
@@ -60,6 +60,7 @@ export default function (providerContext: FtrProviderContext) {
describe('installs packages from direct upload', async () => {
skipIfNoDockerRegistry(providerContext);
setupFleetAndAgents(providerContext);
+
afterEach(async () => {
if (server) {
// remove the packages just in case it being installed will affect other tests
diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_error_rollback.ts b/x-pack/test/fleet_api_integration/apis/epm/install_error_rollback.ts
index fba01e840cfd0..7649237f0ab4d 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/install_error_rollback.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/install_error_rollback.ts
@@ -30,12 +30,15 @@ export default function (providerContext: FtrProviderContext) {
};
const getPackageInfo = async (pkg: string, version: string) => {
- return await supertest.get(`/api/fleet/epm/packages/${pkg}/${version}`).set('kbn-xsrf', 'xxxx');
+ return await supertest
+ .get(`/api/fleet/epm/packages/${pkg}/${version}?prerelease=true`)
+ .set('kbn-xsrf', 'xxxx');
};
describe('package installation error handling and rollback', async () => {
skipIfNoDockerRegistry(providerContext);
setupFleetAndAgents(providerContext);
+
beforeEach(async () => {
await kibanaServer.savedObjects.cleanStandardList();
});
diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_overrides.ts b/x-pack/test/fleet_api_integration/apis/epm/install_overrides.ts
index d33f9b8a922d9..b89c036e84a9d 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/install_overrides.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/install_overrides.ts
@@ -26,6 +26,7 @@ export default function (providerContext: FtrProviderContext) {
describe('installs packages that include settings and mappings overrides', async () => {
skipIfNoDockerRegistry(providerContext);
setupFleetAndAgents(providerContext);
+
after(async () => {
if (server.enabled) {
// remove the package just in case it being installed will affect other tests
diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_prerelease.ts b/x-pack/test/fleet_api_integration/apis/epm/install_prerelease.ts
index f375c9902317f..86e91d6707c5a 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/install_prerelease.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/install_prerelease.ts
@@ -25,6 +25,7 @@ export default function (providerContext: FtrProviderContext) {
describe('installs package that has a prerelease version', async () => {
skipIfNoDockerRegistry(providerContext);
setupFleetAndAgents(providerContext);
+
after(async () => {
if (server.enabled) {
// remove the package just in case it being installed will affect other tests
diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_remove_kbn_assets_in_space.ts b/x-pack/test/fleet_api_integration/apis/epm/install_remove_kbn_assets_in_space.ts
index 9e364f28f8b3e..08740ea5335b2 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/install_remove_kbn_assets_in_space.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/install_remove_kbn_assets_in_space.ts
@@ -50,6 +50,7 @@ export default function (providerContext: FtrProviderContext) {
describe('installs and uninstalls all assets (non default space)', async () => {
skipIfNoDockerRegistry(providerContext);
setupFleetAndAgents(providerContext);
+
before(async () => {
await createSpace(testSpaceId);
});
diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_remove_multiple.ts b/x-pack/test/fleet_api_integration/apis/epm/install_remove_multiple.ts
index 275a2abf744bc..48071d15436fd 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/install_remove_multiple.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/install_remove_multiple.ts
@@ -62,6 +62,7 @@ export default function (providerContext: FtrProviderContext) {
describe('installs and uninstalls multiple packages side effects', async () => {
skipIfNoDockerRegistry(providerContext);
setupFleetAndAgents(providerContext);
+
before(async () => {
if (!server.enabled) return;
await installPackages([
diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_tag_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/install_tag_assets.ts
index 7458912207a38..aca56f5fce936 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/install_tag_assets.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/install_tag_assets.ts
@@ -68,6 +68,7 @@ export default function (providerContext: FtrProviderContext) {
describe('asset tagging', () => {
skipIfNoDockerRegistry(providerContext);
setupFleetAndAgents(providerContext);
+
before(async () => {
await createSpace(testSpaceId);
});
diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_update.ts b/x-pack/test/fleet_api_integration/apis/epm/install_update.ts
index 669166b189789..c36efd6066b6e 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/install_update.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/install_update.ts
@@ -26,6 +26,7 @@ export default function (providerContext: FtrProviderContext) {
describe('installing and updating scenarios', async () => {
skipIfNoDockerRegistry(providerContext);
setupFleetAndAgents(providerContext);
+
after(async () => {
await deletePackage('multiple_versions', '0.3.0');
});
diff --git a/x-pack/test/fleet_api_integration/apis/epm/list.ts b/x-pack/test/fleet_api_integration/apis/epm/list.ts
index 51f003a7192d5..5727f7130f563 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/list.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/list.ts
@@ -23,6 +23,7 @@ export default function (providerContext: FtrProviderContext) {
describe('EPM - list', async function () {
skipIfNoDockerRegistry(providerContext);
+
before(async () => {
await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server');
});
diff --git a/x-pack/test/fleet_api_integration/apis/epm/package_install_complete.ts b/x-pack/test/fleet_api_integration/apis/epm/package_install_complete.ts
index fdf90c636885d..f29e36daebdd2 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/package_install_complete.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/package_install_complete.ts
@@ -26,6 +26,7 @@ export default function (providerContext: FtrProviderContext) {
describe('setup checks packages completed install', async () => {
skipIfNoDockerRegistry(providerContext);
setupFleetAndAgents(providerContext);
+
describe('package install', async () => {
before(async () => {
if (!server.enabled) return;
diff --git a/x-pack/test/fleet_api_integration/apis/epm/setup.ts b/x-pack/test/fleet_api_integration/apis/epm/setup.ts
index 7720d915f6f13..f930166f4a74f 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/setup.ts
+++ b/x-pack/test/fleet_api_integration/apis/epm/setup.ts
@@ -21,6 +21,7 @@ export default function (providerContext: FtrProviderContext) {
describe('setup api', async () => {
skipIfNoDockerRegistry(providerContext);
setupFleetAndAgents(providerContext);
+
// FLAKY: https://github.com/elastic/kibana/issues/118479
describe.skip('setup performs upgrades', async () => {
const oldEndpointVersion = '0.13.0';
diff --git a/x-pack/test/fleet_api_integration/apis/fleet_setup.ts b/x-pack/test/fleet_api_integration/apis/fleet_setup.ts
index e1e1f90f57eb6..3f76f4594592f 100644
--- a/x-pack/test/fleet_api_integration/apis/fleet_setup.ts
+++ b/x-pack/test/fleet_api_integration/apis/fleet_setup.ts
@@ -69,7 +69,7 @@ export default function (providerContext: FtrProviderContext) {
await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxxx').expect(200);
const { body: apiResponse } = await supertest
- .get(`/api/fleet/epm/packages?experimental=true`)
+ .get(`/api/fleet/epm/packages?prerelease=true`)
.expect(200);
const installedPackages = apiResponse.response
.filter((p: any) => p.status === 'installed')
diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/delete.ts b/x-pack/test/fleet_api_integration/apis/package_policy/delete.ts
index 383a92fa164e8..b15b84dcfbee9 100644
--- a/x-pack/test/fleet_api_integration/apis/package_policy/delete.ts
+++ b/x-pack/test/fleet_api_integration/apis/package_policy/delete.ts
@@ -15,6 +15,7 @@ export default function (providerContext: FtrProviderContext) {
describe('Package Policy - delete', () => {
skipIfNoDockerRegistry(providerContext);
+
describe('Delete one', () => {
let agentPolicy: any;
let packagePolicy: any;
diff --git a/x-pack/test/fleet_api_integration/helpers.ts b/x-pack/test/fleet_api_integration/helpers.ts
index d3b623d0426f3..c21a9e01b3309 100644
--- a/x-pack/test/fleet_api_integration/helpers.ts
+++ b/x-pack/test/fleet_api_integration/helpers.ts
@@ -89,3 +89,19 @@ export async function generateAgent(
refresh: 'wait_for',
});
}
+
+export function setPrereleaseSetting(supertest: any) {
+ before(async () => {
+ await supertest
+ .put('/api/fleet/settings')
+ .set('kbn-xsrf', 'xxxx')
+ .send({ prerelease_integrations_enabled: true });
+ });
+
+ after(async () => {
+ await supertest
+ .put('/api/fleet/settings')
+ .set('kbn-xsrf', 'xxxx')
+ .send({ prerelease_integrations_enabled: false });
+ });
+}
diff --git a/x-pack/test/functional/apps/maps/group4/geofile_wizard_auto_open.ts b/x-pack/test/functional/apps/maps/group4/geofile_wizard_auto_open.ts
index ebe434b6afe6e..d60d7b89a0121 100644
--- a/x-pack/test/functional/apps/maps/group4/geofile_wizard_auto_open.ts
+++ b/x-pack/test/functional/apps/maps/group4/geofile_wizard_auto_open.ts
@@ -22,7 +22,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const geoFileCard = await find.byCssSelector(
'[data-test-subj="integration-card:ui_link:ingest_geojson"]'
);
- geoFileCard.click();
+ await geoFileCard.click();
});
it('should navigate to maps app with url params', async () => {
diff --git a/x-pack/test/functional_synthetics/services/uptime/synthetics_package.ts b/x-pack/test/functional_synthetics/services/uptime/synthetics_package.ts
index 14c7abaa57343..5b9525bf2060b 100644
--- a/x-pack/test/functional_synthetics/services/uptime/synthetics_package.ts
+++ b/x-pack/test/functional_synthetics/services/uptime/synthetics_package.ts
@@ -50,7 +50,7 @@ export function SyntheticsPackageProvider({ getService }: FtrProviderContext) {
apiRequest = retry.try(() => {
return supertest
.get(INGEST_API_EPM_PACKAGES)
- .query({ experimental: true })
+ .query({ prerelease: true })
.set('kbn-xsrf', 'xxx')
.expect(200)
.catch((error) => {