From 60ee8aa35a7a34dd3214464106d669980abedeea Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Thu, 6 Oct 2022 13:38:53 -0400 Subject: [PATCH 01/16] [Fleet] Add UI to edit Fleet server hosts --- .../plugins/fleet/common/services/routes.ts | 12 ++ .../types/rest_spec/fleet_server_hosts.ts | 20 +++ .../fleet_server_hosts_flyout/index.tsx | 136 ++++++++++----- .../use_fleet_server_host_form.tsx | 141 ++++++++-------- .../fleet_server_hosts_table/index.tsx | 156 ++++++++++++++++++ .../fleet_server_hosts_section.tsx | 77 +++++++++ .../components/settings_page/index.tsx | 14 +- .../settings_section.stories.tsx | 44 ----- .../settings_page/settings_section.tsx | 110 ------------ .../fleet/sections/settings/hooks/index.tsx | 6 + .../hooks/use_delete_fleet_server_host.tsx | 70 ++++++++ .../fleet/sections/settings/index.tsx | 49 +++++- .../fleet/public/constants/page_paths.ts | 16 +- .../hooks/use_request/fleet_server_hosts.ts | 45 +++++ .../fleet/public/hooks/use_request/index.ts | 1 + x-pack/plugins/fleet/public/types/index.ts | 1 + .../agent_policies/full_agent_policy.test.ts | 7 +- .../agent_policies/full_agent_policy.ts | 28 +--- .../fleet/server/services/agent_policy.ts | 4 +- .../server/services/fleet_server_host.ts | 14 ++ 20 files changed, 656 insertions(+), 295 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_table/index.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/fleet_server_hosts_section.tsx delete mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/settings_section.stories.tsx delete mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/settings_section.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/settings/hooks/index.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/settings/hooks/use_delete_fleet_server_host.tsx create mode 100644 x-pack/plugins/fleet/public/hooks/use_request/fleet_server_hosts.ts diff --git a/x-pack/plugins/fleet/common/services/routes.ts b/x-pack/plugins/fleet/common/services/routes.ts index c2f76758c3d7b..4c8f053b56cf9 100644 --- a/x-pack/plugins/fleet/common/services/routes.ts +++ b/x-pack/plugins/fleet/common/services/routes.ts @@ -21,6 +21,7 @@ import { K8S_API_ROUTES, PRECONFIGURATION_API_ROUTES, DOWNLOAD_SOURCE_API_ROUTES, + FLEET_SERVER_HOST_API_ROUTES, } from '../constants'; export const epmRouteService = { @@ -218,6 +219,17 @@ export const outputRoutesService = { getCreateLogstashApiKeyPath: () => OUTPUT_API_ROUTES.LOGSTASH_API_KEY_PATTERN, }; +export const fleetServerHostsRoutesService = { + getInfoPath: (itemId: string) => + FLEET_SERVER_HOST_API_ROUTES.INFO_PATTERN.replace('{itemId}', itemId), + getUpdatePath: (itemId: string) => + FLEET_SERVER_HOST_API_ROUTES.UPDATE_PATTERN.replace('{itemId}', itemId), + getListPath: () => FLEET_SERVER_HOST_API_ROUTES.LIST_PATTERN, + getDeletePath: (itemId: string) => + FLEET_SERVER_HOST_API_ROUTES.DELETE_PATTERN.replace('{itemId}', itemId), + getCreatePath: () => FLEET_SERVER_HOST_API_ROUTES.CREATE_PATTERN, +}; + export const settingsRoutesService = { getInfoPath: () => SETTINGS_API_ROUTES.INFO_PATTERN, getUpdatePath: () => SETTINGS_API_ROUTES.UPDATE_PATTERN, diff --git a/x-pack/plugins/fleet/common/types/rest_spec/fleet_server_hosts.ts b/x-pack/plugins/fleet/common/types/rest_spec/fleet_server_hosts.ts index bf8be3cb38407..178e7679947a8 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/fleet_server_hosts.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/fleet_server_hosts.ts @@ -10,3 +10,23 @@ import type { FleetServerHost } from '../models'; import type { ListResult } from './common'; export type GetFleetServerHostsResponse = ListResult; + +export interface PutFleetServerHostsRequest { + params: { + itemId: string; + }; + body: { + name?: string; + host_urls?: string[]; + is_default?: boolean; + }; +} + +export interface PostFleetServerHostsRequest { + body: { + id?: string; + name?: string; + host_urls?: string[]; + is_default?: boolean; + }; +} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/index.tsx index 57c9ded6609b5..5c9a4c49dae65 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/index.tsx @@ -21,71 +21,131 @@ import { EuiButtonEmpty, EuiButton, EuiSpacer, + EuiForm, + EuiFormRow, + EuiFieldText, + EuiSwitch, } from '@elastic/eui'; import { MultiRowInput } from '../multi_row_input'; import { useStartServices } from '../../../../hooks'; import { FLYOUT_MAX_WIDTH } from '../../constants'; +import type { FleetServerHost } from '../../../../types'; import { useFleetServerHostsForm } from './use_fleet_server_host_form'; export interface FleetServerHostsFlyoutProps { onClose: () => void; - fleetServerHosts: string[]; + fleetServerHost?: FleetServerHost; } export const FleetServerHostsFlyout: React.FunctionComponent = ({ onClose, - fleetServerHosts, + fleetServerHost, }) => { const { docLinks } = useStartServices(); - const form = useFleetServerHostsForm(fleetServerHosts, onClose); + const form = useFleetServerHostsForm(fleetServerHost, onClose); + const { inputs } = form; return ( -

- +

+ {fleetServerHost ? ( + + ) : ( + + )}

- - - - - ), - }} - /> - - - + } - )} - /> + {...inputs.nameInput.formRowProps} + > + + + + } + > + <> + + + + + ), + }} + /> + + + + + + + + } + /> + + diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.tsx index bfe6ffd044140..5411244071404 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.tsx @@ -10,10 +10,17 @@ import React, { useCallback, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { sendPutSettings, useComboInput, useStartServices } from '../../../../hooks'; +import { + sendPostFleetServerHost, + sendPutFleetServerHost, + useComboInput, + useInput, + useStartServices, + useSwitchInput, +} from '../../../../hooks'; import { isDiffPathProtocol } from '../../../../../../../common/services'; import { useConfirmModal } from '../../hooks/use_confirm_modal'; -import { getAgentAndPolicyCount } from '../../services/agent_and_policies_count'; +import type { FleetServerHost } from '../../../../types'; const URL_REGEX = /^(https):\/\/[^\s$.?#].[^\s]*$/gm; @@ -24,42 +31,10 @@ const ConfirmTitle = () => ( /> ); -interface ConfirmDescriptionProps { - agentCount: number; - agentPolicyCount: number; -} - -const ConfirmDescription: React.FunctionComponent = ({ - agentCount, - agentPolicyCount, -}) => ( +const ConfirmDescription: React.FunctionComponent = ({}) => ( - - - ), - policies: ( - - - - ), - }} + defaultMessage="This action will update agent policies enrolled in this Fleet Server. This action can not be undone. Are you sure you wish to continue?" /> ); @@ -87,7 +62,7 @@ function validateFleetServerHosts(value: string[]) { } else if (!val.match(URL_REGEX)) { res.push({ message: i18n.translate('xpack.fleet.settings.fleetServerHostsError', { - defaultMessage: 'Invalid URL', + defaultMessage: 'Invalid URL (must be an https URL)', }), index: idx, }); @@ -127,24 +102,41 @@ function validateFleetServerHosts(value: string[]) { } } +export function validateName(value: string) { + if (!value || value === '') { + return [ + i18n.translate('xpack.fleet.settings.fleetServerHost.nameIsRequiredErrorMessage', { + defaultMessage: 'Name is required', + }), + ]; + } +} + export function useFleetServerHostsForm( - fleetServerHostsDefaultValue: string[], + fleetServerHost: FleetServerHost | undefined, onSuccess: () => void ) { const [isLoading, setIsLoading] = useState(false); const { notifications } = useStartServices(); const { confirm } = useConfirmModal(); + const isPreconfigured = fleetServerHost?.is_preconfigured ?? false; + + const nameInput = useInput(fleetServerHost?.name ?? '', validateName, isPreconfigured); + const isDefaultInput = useSwitchInput( + fleetServerHost?.is_default ?? false, + isPreconfigured || fleetServerHost?.is_default + ); - const fleetServerHostsInput = useComboInput( - 'fleetServerHostsInput', - fleetServerHostsDefaultValue, - validateFleetServerHosts + const hostUrlsInput = useComboInput( + 'hostUrls', + fleetServerHost?.host_urls || [], + validateFleetServerHosts, + isPreconfigured ); - const fleetServerHostsInputValidate = fleetServerHostsInput.validate; const validate = useCallback( - () => fleetServerHostsInputValidate(), - [fleetServerHostsInputValidate] + () => hostUrlsInput.validate() && nameInput.validate(), + [hostUrlsInput, nameInput] ); const submit = useCallback(async () => { @@ -152,46 +144,65 @@ export function useFleetServerHostsForm( if (!validate()) { return; } - const { agentCount, agentPolicyCount } = await getAgentAndPolicyCount(); - if ( - !(await confirm( - , - - )) - ) { + if (!(await confirm(, ))) { return; } setIsLoading(true); - const settingsResponse = await sendPutSettings({ - fleet_server_hosts: fleetServerHostsInput.value, - }); - if (settingsResponse.error) { - throw settingsResponse.error; + if (fleetServerHost) { + const res = await sendPutFleetServerHost(fleetServerHost.id, { + name: nameInput.value, + host_urls: hostUrlsInput.value, + is_default: isDefaultInput.value, + }); + if (res.error) { + throw res.error; + } + } else { + const res = await sendPostFleetServerHost({ + name: nameInput.value, + host_urls: hostUrlsInput.value, + is_default: isDefaultInput.value, + }); + if (res.error) { + throw res.error; + } } notifications.toasts.addSuccess( i18n.translate('xpack.fleet.settings.fleetServerHostsFlyout.successToastTitle', { - defaultMessage: 'Settings saved', + defaultMessage: 'Fleet Server host saved', }) ); setIsLoading(false); - onSuccess(); + await onSuccess(); } catch (error) { setIsLoading(false); notifications.toasts.addError(error, { title: i18n.translate('xpack.fleet.settings.fleetServerHostsFlyout.errorToastTitle', { - defaultMessage: 'An error happened while saving settings', + defaultMessage: 'An error happened while saving Fleet Server host', }), }); } - }, [fleetServerHostsInput.value, validate, notifications, confirm, onSuccess]); - - const isDisabled = - isLoading || !fleetServerHostsInput.hasChanged || fleetServerHostsInput.props.isInvalid; + }, [ + fleetServerHost, + nameInput.value, + hostUrlsInput.value, + isDefaultInput.value, + validate, + notifications, + confirm, + onSuccess, + ]); + + const isDisabled = isLoading || !hostUrlsInput.hasChanged || hostUrlsInput.props.isInvalid; return { isLoading, isDisabled, submit, - fleetServerHostsInput, + inputs: { + hostUrlsInput, + nameInput, + isDefaultInput, + }, }; } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_table/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_table/index.tsx new file mode 100644 index 0000000000000..053baaf4e4f8a --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_table/index.tsx @@ -0,0 +1,156 @@ +/* + * 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, { useMemo } from 'react'; +import styled from 'styled-components'; + +import { + EuiBasicTable, + EuiFlexGroup, + EuiFlexItem, + EuiIconTip, + EuiIcon, + EuiButtonIcon, +} from '@elastic/eui'; +import type { EuiBasicTableColumn } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import type { FleetServerHost } from '../../../../types'; +import { useLink } from '../../../../hooks'; + +export interface FleetServerHostsTableProps { + fleetServerHosts: FleetServerHost[]; + deleteFleetServerHost: (fleetServerHost: FleetServerHost) => void; +} + +const NameFlexItemWithMaxWidth = styled(EuiFlexItem)` + max-width: 250px; +`; + +// Allow child to be truncated +const FlexGroupWithMinWidth = styled(EuiFlexGroup)` + min-width: 0px; +`; + +export const FleetServerHostsTable: React.FunctionComponent = ({ + fleetServerHosts, + deleteFleetServerHost, +}) => { + const { getHref } = useLink(); + + const columns = useMemo((): Array> => { + return [ + { + render: (fleetServerHost: FleetServerHost) => ( + + +

+ {fleetServerHost.name} +

+
+ {fleetServerHost.is_preconfigured && ( + + + + )} +
+ ), + width: '288px', + name: i18n.translate('xpack.fleet.settings.fleetServerHostsTable.nameColumnTitle', { + defaultMessage: 'Name', + }), + }, + { + truncateText: true, + field: 'host_urls', + render: (urls: string[]) => ( + + {urls.map((url) => ( + +

+ {url} +

+
+ ))} +
+ ), + name: i18n.translate('xpack.fleet.settings.fleetServerHostsTable.hostUrlsColumnTitle', { + defaultMessage: 'Host URLs', + }), + }, + { + render: (fleetServerHost: FleetServerHost) => + fleetServerHost.is_default ? ( + + ) : null, + width: '200px', + name: i18n.translate('xpack.fleet.settings.fleetServerHostsTable.defaultColumnTitle', { + defaultMessage: 'Default', + }), + }, + { + width: '68px', + render: (fleetServerHost: FleetServerHost) => { + const isDeleteVisible = !fleetServerHost.is_default && !fleetServerHost.is_preconfigured; + + return ( + + + {isDeleteVisible && ( + deleteFleetServerHost(fleetServerHost)} + title={i18n.translate( + 'xpack.fleet.settings.fleetServerHostsTable.deleteButtonTitle', + { + defaultMessage: 'Delete', + } + )} + data-test-subj="fleetServerHostsTable.delete.btn" + /> + )} + + + + + + ); + }, + name: i18n.translate('xpack.fleet.settings.fleetServerHostsTable.actionsColumnTitle', { + defaultMessage: 'Actions', + }), + }, + ]; + }, [getHref, deleteFleetServerHost]); + + return ; +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/fleet_server_hosts_section.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/fleet_server_hosts_section.tsx new file mode 100644 index 0000000000000..0395061078b43 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/fleet_server_hosts_section.tsx @@ -0,0 +1,77 @@ +/* + * 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 from 'react'; + +import { EuiTitle, EuiLink, EuiText, EuiSpacer, EuiButtonEmpty } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import type { Settings, FleetServerHost } from '../../../../types'; +import { useLink, useStartServices } from '../../../../hooks'; +import { FleetServerHostsTable } from '../fleet_server_hosts_table'; + +export interface FleetServerHostsSectionProps { + settings: Settings; + fleetServerHosts: FleetServerHost[]; + deleteFleetServerHost: (fleetServerHost: FleetServerHost) => void; +} + +export const FleetServerHostsSection: React.FunctionComponent = ({ + settings, + fleetServerHosts, + deleteFleetServerHost, +}) => { + const { docLinks } = useStartServices(); + const { getHref } = useLink(); + + return ( + <> + +

+ +

+
+ + + + + + ), + }} + /> + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/index.tsx index 4c5db21725639..7499fefa1971d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/index.tsx @@ -8,16 +8,18 @@ import React from 'react'; import { EuiSpacer } from '@elastic/eui'; -import type { Output, Settings, DownloadSource } from '../../../../types'; +import type { Output, Settings, DownloadSource, FleetServerHost } from '../../../../types'; -import { SettingsSection } from './settings_section'; +import { FleetServerHostsSection } from './fleet_server_hosts_section'; import { OutputSection } from './output_section'; import { AgentBinarySection } from './agent_binary_section'; export interface SettingsPageProps { settings: Settings; outputs: Output[]; + fleetServerHosts: FleetServerHost[]; deleteOutput: (output: Output) => void; + deleteFleetServerHost: (fleetServerHost: FleetServerHost) => void; downloadSources: DownloadSource[]; deleteDownloadSource: (ds: DownloadSource) => void; } @@ -25,14 +27,20 @@ export interface SettingsPageProps { export const SettingsPage: React.FunctionComponent = ({ settings, outputs, + fleetServerHosts, deleteOutput, + deleteFleetServerHost, downloadSources, deleteDownloadSource, }) => { return ( <> - + diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/settings_section.stories.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/settings_section.stories.tsx deleted file mode 100644 index 9eab2479b901a..0000000000000 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/settings_section.stories.tsx +++ /dev/null @@ -1,44 +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 React from 'react'; - -import { SettingsSection as Component } from './settings_section'; - -export default { - component: Component, - title: 'Sections/Fleet/Settings', -}; - -interface Args { - width: number; - fleetServerHosts: string[]; -} - -const args: Args = { - width: 1200, - fleetServerHosts: [ - 'https://myfleetserver:8220', - 'https://alongerfleetserverwithaverylongname:8220', - ], -}; - -export const SettingsSection = ({ width, fleetServerHosts }: Args) => { - return ( -
- -
- ); -}; - -SettingsSection.args = args; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/settings_section.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/settings_section.tsx deleted file mode 100644 index bf471a0acd30a..0000000000000 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/settings_section.tsx +++ /dev/null @@ -1,110 +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 React, { useMemo } from 'react'; -import { - EuiTitle, - EuiLink, - EuiText, - EuiSpacer, - EuiBasicTable, - EuiButtonEmpty, - EuiToolTip, -} from '@elastic/eui'; -import type { EuiBasicTableColumn } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { i18n } from '@kbn/i18n'; - -import type { Settings } from '../../../../types'; -import { useLink, useStartServices } from '../../../../hooks'; - -export interface SettingsSectionProps { - settings: Settings; -} - -export const SettingsSection: React.FunctionComponent = ({ settings }) => { - const { docLinks } = useStartServices(); - const { getHref } = useLink(); - - const columns = useMemo((): Array> => { - return [ - { - render: (host: string) => host, - name: i18n.translate('xpack.fleet.settings.fleetServerHostUrlColumnTitle', { - defaultMessage: 'Host URL', - }), - }, - ]; - }, []); - - const isEditDisabled = settings.preconfigured_fields?.includes('fleet_server_hosts') ?? false; - const BtnWrapper = useMemo((): React.FunctionComponent => { - if (!isEditDisabled) { - return ({ children }) => <>{children}; - } - - return ({ children }) => ( - - } - > - <>{children} - - ); - }, [isEditDisabled]); - - return ( - <> - -

- -

-
- - - - - - ), - }} - /> - - - - - - - - - - - - ); -}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/hooks/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/hooks/index.tsx new file mode 100644 index 0000000000000..1fec1c76430eb --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/hooks/index.tsx @@ -0,0 +1,6 @@ +/* + * 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. + */ diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/hooks/use_delete_fleet_server_host.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/hooks/use_delete_fleet_server_host.tsx new file mode 100644 index 0000000000000..7c2046a01e517 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/hooks/use_delete_fleet_server_host.tsx @@ -0,0 +1,70 @@ +/* + * 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, { useCallback } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; + +import { sendDeleteFleetServerHost, useStartServices } from '../../../hooks'; +import type { FleetServerHost } from '../../../types'; + +import { useConfirmModal } from './use_confirm_modal'; + +const ConfirmTitle = () => ( + +); + +const ConfirmDescription: React.FunctionComponent = ({}) => ( + +); + +export function useDeleteFleetServerHost(onSuccess: () => void) { + const { confirm } = useConfirmModal(); + const { notifications } = useStartServices(); + const deleteFleetServerHost = useCallback( + async (fleetServerHost: FleetServerHost) => { + try { + const isConfirmed = await confirm(, , { + buttonColor: 'danger', + confirmButtonText: i18n.translate( + 'xpack.fleet.settings.deleteFleetServerHosts.confirmButtonLabel', + { + defaultMessage: 'Delete and deploy changes', + } + ), + }); + + if (!isConfirmed) { + return; + } + + const res = await sendDeleteFleetServerHost(fleetServerHost.id); + + if (res.error) { + throw res.error; + } + + onSuccess(); + } catch (err) { + notifications.toasts.addError(err, { + title: i18n.translate('xpack.fleet.settings.deleteFleetServerHosts.errorToastTitle', { + defaultMessage: 'Error deleting Fleet Server hosts', + }), + }); + } + }, + [confirm, notifications.toasts, onSuccess] + ); + + return { deleteFleetServerHost }; +} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/index.tsx index 7a94d9ef4bc79..70999a4bc107d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/index.tsx @@ -9,7 +9,13 @@ import React, { useCallback } from 'react'; import { EuiPortal } from '@elastic/eui'; import { Router, Route, Switch, useHistory, Redirect } from 'react-router-dom'; -import { useBreadcrumbs, useGetOutputs, useGetSettings, useGetDownloadSources } from '../../hooks'; +import { + useBreadcrumbs, + useGetOutputs, + useGetSettings, + useGetDownloadSources, + useGetFleetServerHosts, +} from '../../hooks'; import { FLEET_ROUTING_PATHS, pagePathGetters } from '../../constants'; import { DefaultLayout } from '../../layouts'; import { Loading } from '../../components'; @@ -19,6 +25,7 @@ import { withConfirmModalProvider } from './hooks/use_confirm_modal'; import { FleetServerHostsFlyout } from './components/fleet_server_hosts_flyout'; import { EditOutputFlyout } from './components/edit_output_flyout'; import { useDeleteOutput } from './hooks/use_delete_output'; +import { useDeleteFleetServerHost } from './hooks/use_delete_fleet_server_host'; import { EditDownloadSourceFlyout } from './components/download_source_flyout'; import { useDeleteDownloadSource } from './components/download_source_flyout/use_delete_download_source'; @@ -28,27 +35,39 @@ export const SettingsApp = withConfirmModalProvider(() => { const settings = useGetSettings(); const outputs = useGetOutputs(); + const fleetServerHosts = useGetFleetServerHosts(); const downloadSources = useGetDownloadSources(); const { deleteOutput } = useDeleteOutput(outputs.resendRequest); const { deleteDownloadSource } = useDeleteDownloadSource(downloadSources.resendRequest); + const { deleteFleetServerHost } = useDeleteFleetServerHost(fleetServerHosts.resendRequest); const resendSettingsRequest = settings.resendRequest; const resendOutputRequest = outputs.resendRequest; const resendDownloadSourceRequest = downloadSources.resendRequest; + const resendFleetServerHostsRequest = fleetServerHosts.resendRequest; const onCloseCallback = useCallback(() => { resendSettingsRequest(); resendOutputRequest(); resendDownloadSourceRequest(); + resendFleetServerHostsRequest(); history.replace(pagePathGetters.settings()[1]); - }, [resendSettingsRequest, resendOutputRequest, resendDownloadSourceRequest, history]); + }, [ + resendSettingsRequest, + resendOutputRequest, + resendDownloadSourceRequest, + resendFleetServerHostsRequest, + history, + ]); if ( (settings.isLoading && settings.isInitialRequest) || !settings.data?.item || (outputs.isLoading && outputs.isInitialRequest) || !outputs.data?.items || + (fleetServerHosts.isLoading && fleetServerHosts.isInitialRequest) || + !fleetServerHosts.data?.items || (downloadSources.isLoading && downloadSources.isInitialRequest) || !downloadSources.data?.items ) { @@ -64,11 +83,27 @@ export const SettingsApp = withConfirmModalProvider(() => { + {(route: { match: { params: { itemId: string } } }) => { + const fleetServerHost = fleetServerHosts.data?.items.find( + (o) => route.match.params.itemId === o.id + ); + if (!fleetServerHost) { + return ; + } + + return ( + + + + ); + }} + + - + @@ -119,7 +154,9 @@ export const SettingsApp = withConfirmModalProvider(() => { diff --git a/x-pack/plugins/fleet/public/constants/page_paths.ts b/x-pack/plugins/fleet/public/constants/page_paths.ts index fa9d6c1cec21c..d276d777661ed 100644 --- a/x-pack/plugins/fleet/public/constants/page_paths.ts +++ b/x-pack/plugins/fleet/public/constants/page_paths.ts @@ -16,9 +16,9 @@ export type StaticPage = | 'enrollment_tokens' | 'data_streams' | 'settings' - | 'settings_edit_fleet_server_hosts' | 'settings_create_outputs' | 'settings_create_download_sources' + | 'settings_create_fleet_server_hosts' | 'debug'; export type DynamicPage = @@ -42,7 +42,8 @@ export type DynamicPage = | 'agent_details' | 'agent_details_logs' | 'settings_edit_outputs' - | 'settings_edit_download_sources'; + | 'settings_edit_download_sources' + | 'settings_edit_fleet_server_hosts'; export type Page = StaticPage | DynamicPage; @@ -69,7 +70,8 @@ export const FLEET_ROUTING_PATHS = { enrollment_tokens: '/enrollment-tokens', data_streams: '/data-streams', settings: '/settings', - settings_edit_fleet_server_hosts: '/settings/edit-fleet-server-hosts', + settings_create_fleet_server_hosts: '/settings/create-fleet-server-hosts', + settings_edit_fleet_server_hosts: '/settings/fleet-server-hosts/:itemId', settings_create_outputs: '/settings/create-outputs', settings_edit_outputs: '/settings/outputs/:outputId', settings_create_download_sources: '/settings/create-download-sources', @@ -200,9 +202,13 @@ export const pagePathGetters: { enrollment_tokens: () => [FLEET_BASE_PATH, '/enrollment-tokens'], data_streams: () => [FLEET_BASE_PATH, '/data-streams'], settings: () => [FLEET_BASE_PATH, FLEET_ROUTING_PATHS.settings], - settings_edit_fleet_server_hosts: () => [ + settings_edit_fleet_server_hosts: ({ itemId }) => [ FLEET_BASE_PATH, - FLEET_ROUTING_PATHS.settings_edit_fleet_server_hosts, + FLEET_ROUTING_PATHS.settings_edit_fleet_server_hosts.replace(':itemId', itemId.toString()), + ], + settings_create_fleet_server_hosts: () => [ + FLEET_BASE_PATH, + FLEET_ROUTING_PATHS.settings_create_fleet_server_hosts, ], settings_edit_outputs: ({ outputId }) => [ FLEET_BASE_PATH, diff --git a/x-pack/plugins/fleet/public/hooks/use_request/fleet_server_hosts.ts b/x-pack/plugins/fleet/public/hooks/use_request/fleet_server_hosts.ts new file mode 100644 index 0000000000000..662e0494e20e5 --- /dev/null +++ b/x-pack/plugins/fleet/public/hooks/use_request/fleet_server_hosts.ts @@ -0,0 +1,45 @@ +/* + * 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 { fleetServerHostsRoutesService } from '../../../common/services'; +import type { + GetFleetServerHostsResponse, + PostFleetServerHostsRequest, + PutFleetServerHostsRequest, +} from '../../../common/types/rest_spec/fleet_server_hosts'; + +import { sendRequest, useRequest } from './use_request'; + +export function useGetFleetServerHosts() { + return useRequest({ + method: 'get', + path: fleetServerHostsRoutesService.getListPath(), + }); +} + +export function sendDeleteFleetServerHost(itemId: string) { + return sendRequest({ + method: 'delete', + path: fleetServerHostsRoutesService.getDeletePath(itemId), + }); +} + +export function sendPutFleetServerHost(itemId: string, body: PutFleetServerHostsRequest['body']) { + return sendRequest({ + method: 'put', + path: fleetServerHostsRoutesService.getUpdatePath(itemId), + body, + }); +} + +export function sendPostFleetServerHost(body: PostFleetServerHostsRequest['body']) { + return sendRequest({ + method: 'post', + path: fleetServerHostsRoutesService.getCreatePath(), + body, + }); +} diff --git a/x-pack/plugins/fleet/public/hooks/use_request/index.ts b/x-pack/plugins/fleet/public/hooks/use_request/index.ts index 1ca5297cb22a2..8b1f80b5852fa 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/index.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/index.ts @@ -18,3 +18,4 @@ export * from './setup'; export * from './app'; export * from './ingest_pipelines'; export * from './download_source'; +export * from './fleet_server_hosts'; diff --git a/x-pack/plugins/fleet/public/types/index.ts b/x-pack/plugins/fleet/public/types/index.ts index 2438272503ac7..7554d2ba0f45e 100644 --- a/x-pack/plugins/fleet/public/types/index.ts +++ b/x-pack/plugins/fleet/public/types/index.ts @@ -24,6 +24,7 @@ export type { PackagePolicyPackage, Output, DownloadSource, + FleetServerHost, DataStream, Settings, ActionStatus, diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts index 1059d4a44933f..ca2c910a43f5b 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts @@ -37,12 +37,13 @@ function mockAgentPolicy(data: Partial) { }); } -jest.mock('../settings', () => { +jest.mock('../fleet_server_host', () => { return { - getSettings: () => { + getFleetServerHostsForAgentPolicy: () => { return { id: '93f74c0-e876-11ea-b7d3-8b2acec6f75c', - fleet_server_hosts: ['http://fleetserver:8220'], + is_default: true, + host_urls: ['http://fleetserver:8220'], }; }, }; diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts index 283d3d57faae6..d5f535195f497 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -8,24 +8,18 @@ import type { SavedObjectsClientContract } from '@kbn/core/server'; import { safeLoad } from 'js-yaml'; -import type { - FullAgentPolicy, - PackagePolicy, - Settings, - Output, - FullAgentPolicyOutput, -} from '../../types'; +import type { FullAgentPolicy, PackagePolicy, Output, FullAgentPolicyOutput } from '../../types'; import { agentPolicyService } from '../agent_policy'; import { outputService } from '../output'; import { dataTypes, outputType } from '../../../common/constants'; import type { FullAgentPolicyOutputPermissions, PackageInfo } from '../../../common/types'; -import { getSettings } from '../settings'; import { DEFAULT_OUTPUT } from '../../constants'; import { getSourceUriForAgentPolicy } from '../../routes/agent/source_uri_utils'; import { getPackageInfo } from '../epm/packages'; import { pkgToPkgKey, splitPkgKey } from '../epm/registry'; +import { getFleetServerHostsForAgentPolicy } from '../fleet_server_host'; import { getMonitoringPermissions } from './monitoring_permissions'; import { storedPackagePoliciesToAgentInputs } from '.'; @@ -188,19 +182,13 @@ export async function getFullAgentPolicy( return outputPermissions; }, {}); - // only add settings if not in standalone + // only add fleet server hosts if not in standalone if (!standalone) { - let settings: Settings; - try { - settings = await getSettings(soClient); - } catch (error) { - throw new Error('Default settings is not setup'); - } - if (settings.fleet_server_hosts && settings.fleet_server_hosts.length) { - fullAgentPolicy.fleet = { - hosts: settings.fleet_server_hosts, - }; - } + const fleetServerHost = await getFleetServerHostsForAgentPolicy(soClient, agentPolicy); + + fullAgentPolicy.fleet = { + hosts: fleetServerHost.host_urls, + }; } return fullAgentPolicy; } diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index b9978d85ee73d..a3d54d9659179 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -79,6 +79,7 @@ import { normalizeKuery, escapeSearchQueryPhrase } from './saved_object'; import { appContextService } from './app_context'; import { getFullAgentPolicy } from './agent_policies'; import { validateOutputForPolicy } from './agent_policies'; +import { getDefaultFleetServerHost } from './fleet_server_host'; const SAVED_OBJECT_TYPE = AGENT_POLICY_SAVED_OBJECT_TYPE; @@ -682,8 +683,9 @@ class AgentPolicyService { // Use internal ES client so we have permissions to write to .fleet* indices const esClient = appContextService.getInternalUserESClient(); const defaultOutputId = await outputService.getDefaultDataOutputId(soClient); + const defaultFleetServerHost = await getDefaultFleetServerHost(soClient); - if (!defaultOutputId) { + if (!defaultOutputId || !defaultFleetServerHost) { return; } diff --git a/x-pack/plugins/fleet/server/services/fleet_server_host.ts b/x-pack/plugins/fleet/server/services/fleet_server_host.ts index a3ade854770c3..3d28416b2c871 100644 --- a/x-pack/plugins/fleet/server/services/fleet_server_host.ts +++ b/x-pack/plugins/fleet/server/services/fleet_server_host.ts @@ -19,6 +19,7 @@ import type { FleetServerHostSOAttributes, FleetServerHost, NewFleetServerHost, + AgentPolicy, } from '../types'; import { FleetServerHostUnauthorizedError } from '../errors'; @@ -177,6 +178,19 @@ export async function bulkGetFleetServerHosts( ); } +export async function getFleetServerHostsForAgentPolicy( + soClient: SavedObjectsClientContract, + agentPolicy: AgentPolicy +) { + // TODO support per agent policy + const fleetServerHosts = await getDefaultFleetServerHost(soClient); + if (!fleetServerHosts) { + throw new Error('Default Fleet Server host is not setup'); + } + + return fleetServerHosts; +} + /** * Get the default Fleet server policy hosts or throw if it does not exists */ From 583be598efc37421ec755dfeb9926031fab796ae Mon Sep 17 00:00:00 2001 From: criamico Date: Tue, 11 Oct 2022 10:31:42 +0200 Subject: [PATCH 02/16] Fix jest tests --- .../index.stories.tsx | 12 +++-- .../use_fleet_server_host_form.test.tsx | 52 +++++++++++++------ .../server/services/agent_policy.test.ts | 45 ++++++++++++++++ 3 files changed, 90 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/index.stories.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/index.stories.tsx index c4ff6917867ca..236c60f2d0c40 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/index.stories.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/index.stories.tsx @@ -21,14 +21,18 @@ interface Args { const args: Args = { width: 1200, }; +const fleetServerHost = { + id: 'id1', + name: 'fleet server 1', + host_urls: ['https://host1.fr:8220', 'https://host2-with-a-longer-name.fr:8220'], + is_default: false, + is_preconfigured: false, +}; export const FleetServerHostsFlyout = ({ width }: Args) => { return (
- {}} - fleetServerHosts={['https://host1.fr:8220', 'https://host2-with-a-longer-name.fr:8220']} - /> + {}} fleetServerHost={fleetServerHost} />
); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.test.tsx index aeb49928f3d1e..8df533f0206b7 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.test.tsx @@ -23,16 +23,16 @@ jest.mock('../../hooks/use_confirm_modal', () => ({ describe('useFleetServerHostsForm', () => { it('should not allow to submit an invalid form', async () => { const testRenderer = createFleetTestRendererMock(); - const onSucess = jest.fn(); - const { result } = testRenderer.renderHook(() => useFleetServerHostsForm([], onSucess)); + const onSuccess = jest.fn(); + const { result } = testRenderer.renderHook(() => useFleetServerHostsForm(undefined, onSuccess)); act(() => - result.current.fleetServerHostsInput.props.onChange(['https://test.fr', 'https://test.fr']) + result.current.inputs.hostUrlsInput.props.onChange(['https://test.fr', 'https://test.fr']) ); await act(() => result.current.submit()); - expect(result.current.fleetServerHostsInput.props.errors).toMatchInlineSnapshot(` + expect(result.current.inputs.hostUrlsInput.props.errors).toMatchInlineSnapshot(` Array [ Object { "index": 0, @@ -44,40 +44,62 @@ describe('useFleetServerHostsForm', () => { }, ] `); - expect(onSucess).not.toBeCalled(); + expect(onSuccess).not.toBeCalled(); expect(result.current.isDisabled).toBeTruthy(); }); it('should submit a valid form', async () => { const testRenderer = createFleetTestRendererMock(); - const onSucess = jest.fn(); + const onSuccess = jest.fn(); testRenderer.startServices.http.post.mockResolvedValue({}); - const { result } = testRenderer.renderHook(() => useFleetServerHostsForm([], onSucess)); + const { result } = testRenderer.renderHook(() => + useFleetServerHostsForm( + { + id: 'id1', + name: 'fleet server 1', + host_urls: [], + is_default: false, + is_preconfigured: false, + }, + onSuccess + ) + ); - act(() => result.current.fleetServerHostsInput.props.onChange(['https://test.fr'])); + act(() => result.current.inputs.hostUrlsInput.props.onChange(['https://test.fr'])); await act(() => result.current.submit()); - expect(onSucess).toBeCalled(); + expect(onSuccess).toBeCalled(); }); it('should allow the user to correct and submit a invalid form', async () => { const testRenderer = createFleetTestRendererMock(); - const onSucess = jest.fn(); + const onSuccess = jest.fn(); testRenderer.startServices.http.post.mockResolvedValue({}); - const { result } = testRenderer.renderHook(() => useFleetServerHostsForm([], onSucess)); + const { result } = testRenderer.renderHook(() => + useFleetServerHostsForm( + { + id: 'id1', + name: 'fleet server 1', + host_urls: [], + is_default: false, + is_preconfigured: false, + }, + onSuccess + ) + ); act(() => - result.current.fleetServerHostsInput.props.onChange(['https://test.fr', 'https://test.fr']) + result.current.inputs.hostUrlsInput.props.onChange(['https://test.fr', 'https://test.fr']) ); await act(() => result.current.submit()); - expect(onSucess).not.toBeCalled(); + expect(onSuccess).not.toBeCalled(); expect(result.current.isDisabled).toBeTruthy(); - act(() => result.current.fleetServerHostsInput.props.onChange(['https://test.fr'])); + act(() => result.current.inputs.hostUrlsInput.props.onChange(['https://test.fr'])); expect(result.current.isDisabled).toBeFalsy(); await act(() => result.current.submit()); - expect(onSucess).toBeCalled(); + expect(onSuccess).toBeCalled(); }); }); diff --git a/x-pack/plugins/fleet/server/services/agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policy.test.ts index 8dd8c885af3e4..5ed4b0a290c93 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.test.ts @@ -391,6 +391,29 @@ describe('agent policy', () => { mockedOutputService.getDefaultDataOutputId.mockResolvedValue('default-output'); mockedGetFullAgentPolicy.mockResolvedValue(null); + const mockFleetServerHost = { + id: 'id1', + name: 'fleet server 1', + host_urls: ['https://host1.fr:8220', 'https://host2-with-a-longer-name.fr:8220'], + is_default: false, + is_preconfigured: false, + }; + soClient.find.mockResolvedValue({ + saved_objects: [ + { + id: 'existing-fleet-server-host', + type: 'fleet-fleet-server-host', + score: 1, + references: [], + version: '1.0.0', + attributes: mockFleetServerHost, + }, + ], + page: 0, + per_page: 0, + total: 0, + }); + const mockSo = { attributes: {}, id: 'policy123', @@ -428,6 +451,28 @@ describe('agent policy', () => { references: [], }; soClient.get.mockResolvedValue(mockSo); + const mockFleetServerHost = { + id: 'id1', + name: 'fleet server 1', + host_urls: ['https://host1.fr:8220', 'https://host2-with-a-longer-name.fr:8220'], + is_default: false, + is_preconfigured: false, + }; + soClient.find.mockResolvedValue({ + saved_objects: [ + { + id: 'existing-fleet-server-host', + type: 'fleet-fleet-server-host', + score: 1, + references: [], + version: '1.0.0', + attributes: mockFleetServerHost, + }, + ], + page: 0, + per_page: 0, + total: 0, + }); soClient.bulkGet.mockResolvedValue({ saved_objects: [mockSo], }); From 8ea731ba49603f3372f39d1ebf75fb0ea9d4a4b1 Mon Sep 17 00:00:00 2001 From: criamico Date: Tue, 11 Oct 2022 12:28:09 +0200 Subject: [PATCH 03/16] Fix translations --- .../plugins/translations/translations/fr-FR.json | 7 ------- .../plugins/translations/translations/ja-JP.json | 7 ------- .../plugins/translations/translations/zh-CN.json | 7 ------- .../functional/es_archives/fleet/agents/data.json | 14 ++++++++++++++ 4 files changed, 14 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 976c14d76903b..58f9fca34eb67 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -12836,9 +12836,6 @@ "xpack.fleet.settings.editOutputFlyout.defaultOutputSwitchLabel": "Choisissez cette sortie par défaut pour les {boldAgentIntegrations}.", "xpack.fleet.settings.editOutputFlyout.logstashHostsInputDescription": "Spécifiez les adresses que vos agents utiliseront pour se connecter à Logstash. {guideLink}.", "xpack.fleet.settings.fleetServerHostSectionSubtitle": "Précisez les URL que vos agents doivent utiliser pour se connecter à un serveur Fleet. Si plusieurs URL existent, Fleet affichera la première URL fournie à des fins d'enregistrement. Pour en savoir plus, consultez le {guideLink}.", - "xpack.fleet.settings.fleetServerHostsFlyout.agentPolicyCount": "{agentPolicyCount, plural, one {# stratégie d’agent} other {# stratégies d’agent}}", - "xpack.fleet.settings.fleetServerHostsFlyout.agentsCount": "{agentCount, plural, one {# agent} other {# agents}}", - "xpack.fleet.settings.fleetServerHostsFlyout.confirmModalText": "Cette action mettra à jour les {policies} et {agents}. Impossible d'annuler cette action. Voulez-vous vraiment continuer ?", "xpack.fleet.settings.fleetServerHostsFlyout.description": "Précisez les URL que vos agents doivent utiliser pour se connecter à un serveur Fleet. Si plusieurs URL existent, Fleet affiche la première URL fournie à des fins d'enregistrement. Le serveur Fleet utilise le port 8220 par défaut. Reportez-vous à {link}.", "xpack.fleet.settings.logstashInstructions.addPipelineStepDescription": "Dans le répertoire de configuration Logstash, ouvrez le fichier {pipelineFile} et ajoutez la configuration suivante. Remplacez le chemin d’accès à votre fichier.", "xpack.fleet.settings.logstashInstructions.description": "Ajoutez une configuration de pipeline Elastic Agent à Logstash pour recevoir les événements du framework Elastic Agent. {documentationLink}.", @@ -13672,7 +13669,6 @@ "xpack.fleet.settings.editOutputFlyout.typeInputPlaceholder": "Indiquer le type", "xpack.fleet.settings.editOutputFlyout.yamlConfigInputLabel": "Configuration YAML avancée", "xpack.fleet.settings.editOutputFlyout.yamlConfigInputPlaceholder": "Ces # paramètres YAML seront ajoutés à la section de sortie de chaque stratégie d’agent.", - "xpack.fleet.settings.fleetServerHostEditButtonLabel": "Modifier les hôtes", "xpack.fleet.settings.fleetServerHostsDifferentPathOrProtocolError": "Le protocole et le chemin doivent être identiques pour chaque URL", "xpack.fleet.settings.fleetServerHostsDuplicateError": "URL en double", "xpack.fleet.settings.fleetServerHostSectionTitle": "Hôtes du serveur Fleet", @@ -13684,11 +13680,8 @@ "xpack.fleet.settings.fleetServerHostsFlyout.fleetServerHostsInputPlaceholder": "Indiquer l’URL de l’hôte", "xpack.fleet.settings.fleetServerHostsFlyout.saveButton": "Enregistrer et appliquer les paramètres", "xpack.fleet.settings.fleetServerHostsFlyout.successToastTitle": "Paramètres enregistrés", - "xpack.fleet.settings.fleetServerHostsFlyout.title": "Hôtes du serveur Fleet", "xpack.fleet.settings.fleetServerHostsFlyout.userGuideLink": "Guide de Fleet et d'Elastic Agent", - "xpack.fleet.settings.fleetServerHostsPreconfiguredTooltipContent": "Les hôtes du serveur Fleet sont configurés en dehors de Fleet. Reportez-vous à votre configuration Kibana pour en savoir plus.", "xpack.fleet.settings.fleetServerHostsRequiredError": "L'URL de l'hôte est requise", - "xpack.fleet.settings.fleetServerHostUrlColumnTitle": "URL de l’hôte", "xpack.fleet.settings.fleetSettingsLink": "En savoir plus", "xpack.fleet.settings.fleetUserGuideLink": "Guide de Fleet et d'Elastic Agent", "xpack.fleet.settings.logstashInstructions.apiKeyStepDescription": "Nous recommandons d'autoriser Logstash à effectuer la sortie vers Elasticsearch avec les privilèges minimum pour Elastic Agent.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e920046b1b016..9f81620f22c0f 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -12822,9 +12822,6 @@ "xpack.fleet.settings.editOutputFlyout.defaultOutputSwitchLabel": "この出力を{boldAgentIntegrations}のデフォルトにします。", "xpack.fleet.settings.editOutputFlyout.logstashHostsInputDescription": "エージェントがLogstashに接続するために使用するアドレスを指定します。{guideLink}。", "xpack.fleet.settings.fleetServerHostSectionSubtitle": "エージェントがFleetサーバーに接続するために使用するURLを指定します。複数のURLが存在する場合、Fleetは登録目的で最初に指定されたURLを表示します。詳細については、{guideLink}を参照してください。", - "xpack.fleet.settings.fleetServerHostsFlyout.agentPolicyCount": "{agentPolicyCount, plural, other {# 件のエージェントポリシー}}", - "xpack.fleet.settings.fleetServerHostsFlyout.agentsCount": "{agentCount, plural, other {# 個のエージェント}}", - "xpack.fleet.settings.fleetServerHostsFlyout.confirmModalText": "{policies}と{agents}が更新されます。このアクションは元に戻せません。続行していいですか?", "xpack.fleet.settings.fleetServerHostsFlyout.description": "エージェントがFleetサーバーに接続するために使用するURLを指定します。複数のURLが存在する場合、Fleetは登録目的で最初に指定されたURLを表示します。Fleetサーバーはデフォルトで8220番ポートを使用します。{link}を参照してください。", "xpack.fleet.settings.logstashInstructions.addPipelineStepDescription": "Logstash構成ディレクトリの{pipelineFile}ファイルを開き、次の構成を追加します。ファイルへのパスを置換します。", "xpack.fleet.settings.logstashInstructions.description": "Elasticエージェントパイプライン構成をLogstashに追加し、Elasticエージェントフレームワークからイベントを受信します。{documentationLink}。", @@ -13658,7 +13655,6 @@ "xpack.fleet.settings.editOutputFlyout.typeInputPlaceholder": "タイプを指定", "xpack.fleet.settings.editOutputFlyout.yamlConfigInputLabel": "詳細YAML構成", "xpack.fleet.settings.editOutputFlyout.yamlConfigInputPlaceholder": "# このYAML設定は、各エージェントポリシーの出力セクションに追加されます。", - "xpack.fleet.settings.fleetServerHostEditButtonLabel": "ホストを編集", "xpack.fleet.settings.fleetServerHostsDifferentPathOrProtocolError": "各URLのプロトコルとパスは同じでなければなりません", "xpack.fleet.settings.fleetServerHostsDuplicateError": "重複するURL", "xpack.fleet.settings.fleetServerHostSectionTitle": "Fleetサーバーホスト", @@ -13670,11 +13666,8 @@ "xpack.fleet.settings.fleetServerHostsFlyout.fleetServerHostsInputPlaceholder": "ホストURLを指定", "xpack.fleet.settings.fleetServerHostsFlyout.saveButton": "設定を保存して適用", "xpack.fleet.settings.fleetServerHostsFlyout.successToastTitle": "設定が保存されました", - "xpack.fleet.settings.fleetServerHostsFlyout.title": "Fleetサーバーホスト", "xpack.fleet.settings.fleetServerHostsFlyout.userGuideLink": "FleetおよびElasticエージェントガイド", - "xpack.fleet.settings.fleetServerHostsPreconfiguredTooltipContent": "Fleet Serverホストは、Fleet外で構成されます。詳細については、Kibana構成を参照してください。", "xpack.fleet.settings.fleetServerHostsRequiredError": "ホストURLは必須です", - "xpack.fleet.settings.fleetServerHostUrlColumnTitle": "ホストURL", "xpack.fleet.settings.fleetSettingsLink": "詳細", "xpack.fleet.settings.fleetUserGuideLink": "FleetおよびElasticエージェントガイド", "xpack.fleet.settings.logstashInstructions.apiKeyStepDescription": "Elasticエージェントの最小限の権限で、LogstashがElasticsearchに出力できるようにすることをお勧めします。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 76bca99b96d6a..0031908b63d36 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -12842,9 +12842,6 @@ "xpack.fleet.settings.editOutputFlyout.defaultOutputSwitchLabel": "将此输出设为 {boldAgentIntegrations} 的默认值。", "xpack.fleet.settings.editOutputFlyout.logstashHostsInputDescription": "指定代理将用于连接到 Logstash 的地址。{guideLink}。", "xpack.fleet.settings.fleetServerHostSectionSubtitle": "指定代理用于连接 Fleet 服务器的 URL。如果存在多个 URL,Fleet 将显示提供的第一个 URL 用于注册。有关详细信息,请参阅 {guideLink}。", - "xpack.fleet.settings.fleetServerHostsFlyout.agentPolicyCount": "{agentPolicyCount, plural, other {# 个代理策略}}", - "xpack.fleet.settings.fleetServerHostsFlyout.agentsCount": "{agentCount, plural, other {# 个代理}}", - "xpack.fleet.settings.fleetServerHostsFlyout.confirmModalText": "此操作将更新 {policies} 和 {agents}。此操作无法撤消。是否确定要继续?", "xpack.fleet.settings.fleetServerHostsFlyout.description": "指定代理用于连接 Fleet 服务器的 URL。如果多个 URL 存在,Fleet 显示提供的第一个 URL 用于注册。Fleet 服务器默认使用端口 8220。请参阅 {link}。", "xpack.fleet.settings.logstashInstructions.addPipelineStepDescription": "在 Logstash 配置目录中,打开 {pipelineFile} 文件并添加以下配置。替换您文件的路径。", "xpack.fleet.settings.logstashInstructions.description": "将 Elastic 代理管道配置添加到 Logstash,以从 Elastic 代理框架接收事件。{documentationLink}。", @@ -13678,7 +13675,6 @@ "xpack.fleet.settings.editOutputFlyout.typeInputPlaceholder": "指定类型", "xpack.fleet.settings.editOutputFlyout.yamlConfigInputLabel": "高级 YAML 配置", "xpack.fleet.settings.editOutputFlyout.yamlConfigInputPlaceholder": "此处的 # 个 YAML 设置将添加到每个代理策略的输出部分。", - "xpack.fleet.settings.fleetServerHostEditButtonLabel": "编辑主机", "xpack.fleet.settings.fleetServerHostsDifferentPathOrProtocolError": "对于每个 URL,协议和路径必须相同", "xpack.fleet.settings.fleetServerHostsDuplicateError": "复制 URL", "xpack.fleet.settings.fleetServerHostSectionTitle": "Fleet 服务器主机", @@ -13690,11 +13686,8 @@ "xpack.fleet.settings.fleetServerHostsFlyout.fleetServerHostsInputPlaceholder": "指定主机 URL", "xpack.fleet.settings.fleetServerHostsFlyout.saveButton": "保存并应用设置", "xpack.fleet.settings.fleetServerHostsFlyout.successToastTitle": "设置已保存", - "xpack.fleet.settings.fleetServerHostsFlyout.title": "Fleet 服务器主机", "xpack.fleet.settings.fleetServerHostsFlyout.userGuideLink": "Fleet 和 Elastic 代理指南", - "xpack.fleet.settings.fleetServerHostsPreconfiguredTooltipContent": "Fleet 服务器主机在 Fleet 以外进行配置。请参阅 Kibana 配置了解详情。", "xpack.fleet.settings.fleetServerHostsRequiredError": "主机 URL 必填", - "xpack.fleet.settings.fleetServerHostUrlColumnTitle": "主机 URL", "xpack.fleet.settings.fleetSettingsLink": "了解详情", "xpack.fleet.settings.fleetUserGuideLink": "Fleet 和 Elastic 代理指南", "xpack.fleet.settings.logstashInstructions.apiKeyStepDescription": "建议授权 Logstash 以 Elastic 代理的最低权限输出到 Elasticsearch。", diff --git a/x-pack/test/functional/es_archives/fleet/agents/data.json b/x-pack/test/functional/es_archives/fleet/agents/data.json index 8708710321088..14f3704cdb623 100644 --- a/x-pack/test/functional/es_archives/fleet/agents/data.json +++ b/x-pack/test/functional/es_archives/fleet/agents/data.json @@ -89,6 +89,20 @@ } } +{ + "type": "doc", + "value": { + "id": "fleet-server-host-1", + "index": ".fleet-fleet-server-host", + "source": { + "id": "test-default-123", + "name": "Default", + "is_default": true, + "host_urls": ["https://test.fr:8080", "https://test.fr:8081"] + } + } +} + { "type": "doc", "value": { From f38eccb56cfd23999bf7e3c93f846e91a28fd45f Mon Sep 17 00:00:00 2001 From: criamico Date: Tue, 11 Oct 2022 17:06:14 +0200 Subject: [PATCH 04/16] Add agent policy handling --- x-pack/plugins/fleet/common/types/models/agent_policy.ts | 1 + .../plugins/fleet/server/services/fleet_server_host.ts | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index ea22f73a2e5f9..8389cb07da15c 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -29,6 +29,7 @@ export interface NewAgentPolicy { data_output_id?: string | null; monitoring_output_id?: string | null; download_source_id?: string | null; + fleet_server_host_id?: string | null; schema_version?: string; } diff --git a/x-pack/plugins/fleet/server/services/fleet_server_host.ts b/x-pack/plugins/fleet/server/services/fleet_server_host.ts index 3d28416b2c871..ff870374e6d16 100644 --- a/x-pack/plugins/fleet/server/services/fleet_server_host.ts +++ b/x-pack/plugins/fleet/server/services/fleet_server_host.ts @@ -182,13 +182,14 @@ export async function getFleetServerHostsForAgentPolicy( soClient: SavedObjectsClientContract, agentPolicy: AgentPolicy ) { - // TODO support per agent policy - const fleetServerHosts = await getDefaultFleetServerHost(soClient); - if (!fleetServerHosts) { + const defaultFleetServerHost = await getDefaultFleetServerHost(soClient); + if (!defaultFleetServerHost) { throw new Error('Default Fleet Server host is not setup'); } + const fleetServerHostId = agentPolicy.fleet_server_host_id || defaultFleetServerHost.id; + const fleetServerHost = await getFleetServerHost(soClient, fleetServerHostId); - return fleetServerHosts; + return fleetServerHost; } /** From bf0aa594cbc5cb42f4698e5bca4e112b00e1e8c1 Mon Sep 17 00:00:00 2001 From: criamico Date: Wed, 19 Oct 2022 17:38:24 +0200 Subject: [PATCH 05/16] Update quick start form --- .../advanced_tab.tsx | 2 +- .../components/fleet_server_host_combobox.tsx | 12 +- .../hooks/use_fleet_server_host.ts | 122 ++++++++---------- .../hooks/use_quick_start_form.ts | 69 +++++++--- .../quick_start_tab.tsx | 22 +++- .../steps/get_started.tsx | 99 +++++++++----- .../use_fleet_server_host_form.tsx | 4 +- 7 files changed, 197 insertions(+), 133 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx index e13d9b5394dc9..c0d0cec2fb5a2 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx @@ -63,7 +63,7 @@ export const AdvancedTab: React.FunctionComponent = ({ selecte getInstallFleetServerStep({ isFleetServerReady, serviceToken, - fleetServerHost: fleetServerHostForm.fleetServerHost, + fleetServerHost: fleetServerHostForm.fleetServerHost?.host_urls[0], fleetServerPolicyId: fleetServerPolicyId || selectedPolicyId, deploymentMode, disabled: !Boolean(serviceToken), diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/fleet_server_host_combobox.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/fleet_server_host_combobox.tsx index 0508bd9108d3a..ca9e9755496ce 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/fleet_server_host_combobox.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/fleet_server_host_combobox.tsx @@ -6,12 +6,15 @@ */ import React, { useState } from 'react'; import type { EuiComboBoxOptionOption } from '@elastic/eui'; + import { EuiComboBox, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; +import type { FleetServerHost } from '../../../types'; + interface Props { - fleetServerHost: string | undefined; + fleetServerHost: FleetServerHost | undefined; fleetServerHostSettings: string[]; isDisabled: boolean; isInvalid: boolean; @@ -42,7 +45,6 @@ export const FleetServerHostComboBox: React.FunctionComponent = ({ setCreatedOptions([...createdOptions, option]); onFleetServerHostChange(option); }; - return ( fullWidth @@ -57,7 +59,11 @@ export const FleetServerHostComboBox: React.FunctionComponent = ({ values: { searchValuePlaceholder: '{searchValue}' }, } )} - selectedOptions={fleetServerHost ? [{ label: fleetServerHost, value: fleetServerHost }] : []} + selectedOptions={ + fleetServerHost + ? [{ label: fleetServerHost.host_urls[0], value: fleetServerHost.host_urls[0] }] + : [] + } prepend={ Promise; - fleetServerHost?: string; + saveFleetServerHost: (host: FleetServerHost) => Promise; + fleetServerHost?: FleetServerHost; fleetServerHostSettings: string[]; isFleetServerHostSubmitted: boolean; - setFleetServerHost: React.Dispatch>; + setFleetServerHost: React.Dispatch>; error?: string; - validateFleetServerHost: () => boolean; } export const useFleetServerHost = (): FleetServerHostForm => { - const [fleetServerHost, setFleetServerHost] = useState(); + const [fleetServerHost, setFleetServerHost] = useState(); const [isFleetServerHostSubmitted, setIsFleetServerHostSubmitted] = useState(false); const [error, setError] = useState(); - const { data: settings } = useGetSettings(); + const { data } = useGetFleetServerHosts(); useEffect(() => { - const settingsFleetServerHosts = settings?.item.fleet_server_hosts ?? []; - - if (settingsFleetServerHosts.length) { - setFleetServerHost(settingsFleetServerHosts[0]); + const fleetServerHosts = data?.items ?? []; + const defaultHost = fleetServerHosts.find((item) => item.is_default === true); + + // Get the default host, otherwise the first fleet server found + if (defaultHost) { + setFleetServerHost(defaultHost); + } else { + setFleetServerHost(fleetServerHosts[0]); } - }, [settings?.item.fleet_server_hosts]); - - const validateFleetServerHost = useCallback(() => { - if (!fleetServerHost) { - setError( - i18n.translate('xpack.fleet.fleetServerHost.requiredError', { - defaultMessage: 'Fleet server host is required.', - }) - ); - - return false; - } else if (!fleetServerHost.startsWith('https')) { - setError( - i18n.translate('xpack.fleet.fleetServerHost.requiresHttpsError', { - defaultMessage: 'Fleet server host must begin with "https"', - }) - ); - - return false; - } else if (!fleetServerHost.match(URL_REGEX)) { - setError( - i18n.translate('xpack.fleet.fleetServerSetup.addFleetServerHostInvalidUrlError', { - defaultMessage: 'Invalid URL', - }) - ); - - return false; - } - - return true; - }, [fleetServerHost]); - - const saveFleetServerHost = useCallback(async () => { - setIsFleetServerHostSubmitted(false); - - if (!validateFleetServerHost()) { - return; - } - - // If the Fleet Server host provided already exists in settings, don't submit it - if (settings?.item.fleet_server_hosts.includes(fleetServerHost!)) { + }, [data?.items, fleetServerHost]); + + const saveFleetServerHost = useCallback( + async (newFleetServerHost: FleetServerHost) => { + setIsFleetServerHostSubmitted(false); + setFleetServerHost(newFleetServerHost); + + // if (!isValid) { + // console.log('not valid, return'); + // return; + // } + + const fleetServerHostExists = data?.items.reduce((acc, curr) => { + const hostsIntersection = intersection(curr.host_urls, newFleetServerHost?.host_urls); + return hostsIntersection.length > 0 || acc; + }, false); + + // If the Fleet Server host provided already exists in settings, don't submit it + if (fleetServerHostExists) { + setIsFleetServerHostSubmitted(true); + return; + } + if (newFleetServerHost) { + const res = await sendPostFleetServerHost({ + name: newFleetServerHost?.name, + host_urls: newFleetServerHost?.host_urls, + is_default: newFleetServerHost?.is_default, + }); + if (res.error) { + throw res.error; + } + } setIsFleetServerHostSubmitted(true); - return; - } - - const res = await sendPutSettings({ - fleet_server_hosts: [fleetServerHost!, ...(settings?.item.fleet_server_hosts || [])], - }); - - if (res.error) { - throw res.error; - } - - setIsFleetServerHostSubmitted(true); - }, [fleetServerHost, settings?.item.fleet_server_hosts, validateFleetServerHost]); + }, + [data?.items] + ); return { saveFleetServerHost, fleetServerHost, - fleetServerHostSettings: settings?.item.fleet_server_hosts ?? [], + fleetServerHostSettings: data?.items[0]?.host_urls ?? [], isFleetServerHostSubmitted, setFleetServerHost, error, - validateFleetServerHost, }; }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts index 84fd39aeec378..c8d23051cc407 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts @@ -8,10 +8,23 @@ import { useState, useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; -import { sendCreateAgentPolicy, sendGetOneAgentPolicy, useStartServices } from '../../../hooks'; +import { + sendCreateAgentPolicy, + sendGetOneAgentPolicy, + useStartServices, + useComboInput, + useInput, +} from '../../../hooks'; import type { NewAgentPolicy } from '../../../types'; +import type { FleetServerHost } from '../../../types'; + +import { + validateName, + validateFleetServerHosts, +} from '../../../sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form'; + import { useSelectFleetServerPolicy } from './use_select_fleet_server_policy'; import { useServiceToken } from './use_service_token'; import { useFleetServerHost } from './use_fleet_server_host'; @@ -32,12 +45,11 @@ export interface QuickStartCreateForm { status: QuickStartCreateFormStatus; error?: string; submit: () => void; - fleetServerHost?: string; - fleetServerHostSettings: string[]; + fleetServerHost?: FleetServerHost; isFleetServerHostSubmitted: boolean; - onFleetServerHostChange: (value: string) => void; fleetServerPolicyId?: string; serviceToken?: string; + inputs: any; } /** @@ -52,12 +64,10 @@ export const useQuickStartCreateForm = (): QuickStartCreateForm => { const { fleetServerHost, - fleetServerHostSettings, isFleetServerHostSubmitted, - setFleetServerHost, - validateFleetServerHost, saveFleetServerHost, error: fleetServerError, + setFleetServerHost, } = useFleetServerHost(); // When a validation error is surfaced from the Fleet Server host form, we want to treat it @@ -71,18 +81,34 @@ export const useQuickStartCreateForm = (): QuickStartCreateForm => { const { fleetServerPolicyId, setFleetServerPolicyId } = useSelectFleetServerPolicy(); const { serviceToken, generateServiceToken } = useServiceToken(); - const onFleetServerHostChange = useCallback( - (value: string) => { - setFleetServerHost(value); - }, - [setFleetServerHost] + const isPreconfigured = fleetServerHost?.is_preconfigured ?? false; + const nameInput = useInput(fleetServerHost?.name ?? '', validateName, isPreconfigured); + + const hostUrlsInput = useComboInput( + 'hostUrls', + fleetServerHost?.host_urls || [], + validateFleetServerHosts, + isPreconfigured + ); + const validate = useCallback( + () => hostUrlsInput.validate() && nameInput.validate(), + [hostUrlsInput, nameInput] ); const submit = useCallback(async () => { try { - if (validateFleetServerHost()) { + if (validate()) { setStatus('loading'); - await saveFleetServerHost(); + + const newFleetServerHost = { + name: nameInput.value, + host_urls: hostUrlsInput.value, + is_default: true, + id: 'fleet-server-host', + is_preconfigured: false, + }; + setFleetServerHost(newFleetServerHost); + await saveFleetServerHost(newFleetServerHost); await generateServiceToken(); const existingPolicy = await sendGetOneAgentPolicy( @@ -99,11 +125,9 @@ export const useQuickStartCreateForm = (): QuickStartCreateForm => { withSysMonitoring: true, } ); - setFleetServerPolicyId(createPolicyResponse.data?.item.id); } - setFleetServerHost(fleetServerHost); setStatus('success'); } } catch (err) { @@ -117,13 +141,14 @@ export const useQuickStartCreateForm = (): QuickStartCreateForm => { setError(err.message); } }, [ - validateFleetServerHost, + nameInput.value, + hostUrlsInput.value, + setFleetServerHost, saveFleetServerHost, generateServiceToken, - setFleetServerHost, - fleetServerHost, setFleetServerPolicyId, notifications.toasts, + validate, ]); return { @@ -132,9 +157,11 @@ export const useQuickStartCreateForm = (): QuickStartCreateForm => { submit, fleetServerPolicyId, fleetServerHost, - fleetServerHostSettings, isFleetServerHostSubmitted, - onFleetServerHostChange, serviceToken, + inputs: { + hostUrlsInput, + nameInput, + }, }; }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx index 758a34113efcd..d6e28a684f302 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx @@ -17,24 +17,32 @@ import { } from './steps'; export const QuickStartTab: React.FunctionComponent = () => { - const quickStartCreateForm = useQuickStartCreateForm(); + const { fleetServerHost, fleetServerPolicyId, serviceToken, status, error, submit, inputs } = + useQuickStartCreateForm(); const { isFleetServerReady } = useWaitForFleetServer(); const steps = [ getGettingStartedStep({ - quickStartCreateForm, + fleetServerHost, + fleetServerPolicyId, + serviceToken, + status, + error, + submit, + isFleetServerHostSubmitted: false, + inputs, }), getInstallFleetServerStep({ isFleetServerReady, - fleetServerHost: quickStartCreateForm.fleetServerHost, - fleetServerPolicyId: quickStartCreateForm.fleetServerPolicyId, - serviceToken: quickStartCreateForm.serviceToken, + fleetServerHost: fleetServerHost?.host_urls[0], + fleetServerPolicyId, + serviceToken, deploymentMode: 'quickstart', - disabled: quickStartCreateForm.status !== 'success', + disabled: status !== 'success', }), getConfirmFleetServerConnectionStep({ isFleetServerReady, - disabled: quickStartCreateForm.status !== 'success', + disabled: status !== 'success', }), ]; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/get_started.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/get_started.tsx index 79acb26802499..fe636fffe6b6d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/get_started.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/get_started.tsx @@ -12,46 +12,43 @@ import { EuiButton, EuiCallOut, EuiCode, - EuiFlexGroup, - EuiFlexItem, EuiForm, EuiFormErrorText, EuiLink, EuiSpacer, EuiText, + EuiFormRow, + EuiFieldText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { css } from '@emotion/react'; + +import { MultiRowInput } from '../../../sections/settings/components/multi_row_input'; import { useLink } from '../../../hooks'; import type { QuickStartCreateForm } from '../hooks'; -import { FleetServerHostComboBox } from '../components'; -export function getGettingStartedStep({ - quickStartCreateForm, -}: { - quickStartCreateForm: QuickStartCreateForm; -}): EuiStepProps { +export function getGettingStartedStep(props: QuickStartCreateForm): EuiStepProps { return { title: i18n.translate('xpack.fleet.fleetServerFlyout.getStartedTitle', { defaultMessage: 'Get started with Fleet Server', }), - status: quickStartCreateForm.status === 'success' ? 'complete' : 'current', - children: , + status: props.status === 'success' ? 'complete' : 'current', + children: , }; } -const GettingStartedStepContent: React.FunctionComponent<{ - quickStartCreateForm: QuickStartCreateForm; -}> = ({ quickStartCreateForm }) => { +const GettingStartedStepContent: React.FunctionComponent = ({ + fleetServerHost, + status, + error, + inputs, + submit, +}) => { const { getHref } = useLink(); - const { fleetServerHost, fleetServerHostSettings, onFleetServerHostChange } = - quickStartCreateForm; - - if (quickStartCreateForm.status === 'success') { + if (status === 'success') { return ( {fleetServerHost}, + hostUrl: {fleetServerHost?.host_urls[0]}, fleetSettingsLink: ( - - + + {/* - {quickStartCreateForm.status === 'error' && ( - {quickStartCreateForm.error} - )} + {status === 'error' && {error}} - + */} + + } + {...inputs.nameInput.formRowProps} + > + + + + } + > + <> + + {status === 'error' && {error}} + + ( /> ); -function validateFleetServerHosts(value: string[]) { +export function validateFleetServerHosts(value: string[]) { if (value.length === 0) { return [ { From 5b4e681ddfcd05813ed424a08d21986b0f2d8fba Mon Sep 17 00:00:00 2001 From: criamico Date: Mon, 24 Oct 2022 17:12:27 +0200 Subject: [PATCH 06/16] Update advanced form --- .../hooks/use_fleet_server_host.ts | 45 +++++-- .../hooks/use_quick_start_form.ts | 50 +++----- .../steps/add_fleet_server_host.tsx | 116 ++++++++++-------- .../steps/get_started.tsx | 20 +-- 4 files changed, 112 insertions(+), 119 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_fleet_server_host.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_fleet_server_host.ts index 5d69726683040..23f8942454b17 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_fleet_server_host.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_fleet_server_host.ts @@ -8,22 +8,49 @@ import { useCallback, useEffect, useState } from 'react'; import { intersection } from 'lodash'; -import { sendPostFleetServerHost, useGetFleetServerHosts } from '../../../hooks'; +import { + sendPostFleetServerHost, + useGetFleetServerHosts, + useComboInput, + useInput, +} from '../../../hooks'; import type { FleetServerHost } from '../../../types'; +import { + validateName, + validateFleetServerHosts, +} from '../../../sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form'; + export interface FleetServerHostForm { saveFleetServerHost: (host: FleetServerHost) => Promise; fleetServerHost?: FleetServerHost; - fleetServerHostSettings: string[]; isFleetServerHostSubmitted: boolean; setFleetServerHost: React.Dispatch>; + validate: () => boolean; error?: string; + inputs: { + hostUrlsInput: ReturnType; + nameInput: ReturnType; + }; } export const useFleetServerHost = (): FleetServerHostForm => { const [fleetServerHost, setFleetServerHost] = useState(); const [isFleetServerHostSubmitted, setIsFleetServerHostSubmitted] = useState(false); - const [error, setError] = useState(); + + const isPreconfigured = fleetServerHost?.is_preconfigured ?? false; + const nameInput = useInput(fleetServerHost?.name ?? '', validateName, isPreconfigured); + + const hostUrlsInput = useComboInput( + 'hostUrls', + fleetServerHost?.host_urls || [], + validateFleetServerHosts, + isPreconfigured + ); + const validate = useCallback( + () => hostUrlsInput.validate() && nameInput.validate(), + [hostUrlsInput, nameInput] + ); const { data } = useGetFleetServerHosts(); @@ -44,11 +71,6 @@ export const useFleetServerHost = (): FleetServerHostForm => { setIsFleetServerHostSubmitted(false); setFleetServerHost(newFleetServerHost); - // if (!isValid) { - // console.log('not valid, return'); - // return; - // } - const fleetServerHostExists = data?.items.reduce((acc, curr) => { const hostsIntersection = intersection(curr.host_urls, newFleetServerHost?.host_urls); return hostsIntersection.length > 0 || acc; @@ -77,9 +99,12 @@ export const useFleetServerHost = (): FleetServerHostForm => { return { saveFleetServerHost, fleetServerHost, - fleetServerHostSettings: data?.items[0]?.host_urls ?? [], isFleetServerHostSubmitted, setFleetServerHost, - error, + validate, + inputs: { + hostUrlsInput, + nameInput, + }, }; }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts index c8d23051cc407..f3167b977d312 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts @@ -8,23 +8,13 @@ import { useState, useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; -import { - sendCreateAgentPolicy, - sendGetOneAgentPolicy, - useStartServices, - useComboInput, - useInput, -} from '../../../hooks'; +import type { useComboInput, useInput } from '../../../hooks'; +import { sendCreateAgentPolicy, sendGetOneAgentPolicy, useStartServices } from '../../../hooks'; import type { NewAgentPolicy } from '../../../types'; import type { FleetServerHost } from '../../../types'; -import { - validateName, - validateFleetServerHosts, -} from '../../../sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form'; - import { useSelectFleetServerPolicy } from './use_select_fleet_server_policy'; import { useServiceToken } from './use_service_token'; import { useFleetServerHost } from './use_fleet_server_host'; @@ -49,7 +39,10 @@ export interface QuickStartCreateForm { isFleetServerHostSubmitted: boolean; fleetServerPolicyId?: string; serviceToken?: string; - inputs: any; + inputs: { + hostUrlsInput: ReturnType; + nameInput: ReturnType; + }; } /** @@ -68,6 +61,8 @@ export const useQuickStartCreateForm = (): QuickStartCreateForm => { saveFleetServerHost, error: fleetServerError, setFleetServerHost, + validate, + inputs, } = useFleetServerHost(); // When a validation error is surfaced from the Fleet Server host form, we want to treat it @@ -81,28 +76,14 @@ export const useQuickStartCreateForm = (): QuickStartCreateForm => { const { fleetServerPolicyId, setFleetServerPolicyId } = useSelectFleetServerPolicy(); const { serviceToken, generateServiceToken } = useServiceToken(); - const isPreconfigured = fleetServerHost?.is_preconfigured ?? false; - const nameInput = useInput(fleetServerHost?.name ?? '', validateName, isPreconfigured); - - const hostUrlsInput = useComboInput( - 'hostUrls', - fleetServerHost?.host_urls || [], - validateFleetServerHosts, - isPreconfigured - ); - const validate = useCallback( - () => hostUrlsInput.validate() && nameInput.validate(), - [hostUrlsInput, nameInput] - ); - const submit = useCallback(async () => { try { if (validate()) { setStatus('loading'); const newFleetServerHost = { - name: nameInput.value, - host_urls: hostUrlsInput.value, + name: inputs.nameInput.value, + host_urls: inputs.hostUrlsInput.value, is_default: true, id: 'fleet-server-host', is_preconfigured: false, @@ -141,14 +122,14 @@ export const useQuickStartCreateForm = (): QuickStartCreateForm => { setError(err.message); } }, [ - nameInput.value, - hostUrlsInput.value, + validate, + inputs.nameInput.value, + inputs.hostUrlsInput.value, setFleetServerHost, saveFleetServerHost, generateServiceToken, setFleetServerPolicyId, notifications.toasts, - validate, ]); return { @@ -159,9 +140,6 @@ export const useQuickStartCreateForm = (): QuickStartCreateForm => { fleetServerHost, isFleetServerHostSubmitted, serviceToken, - inputs: { - hostUrlsInput, - nameInput, - }, + inputs, }; }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx index 62b11e3295ecf..d651a6d084cd7 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx @@ -11,20 +11,22 @@ import { EuiButton, EuiCallOut, EuiCode, - EuiFlexGroup, - EuiFlexItem, EuiForm, EuiFormErrorText, EuiLink, EuiSpacer, EuiText, + EuiFormRow, + EuiFieldText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { FleetServerHost } from '../../../types'; + import { useStartServices, useLink } from '../../../hooks'; import type { FleetServerHostForm } from '../hooks'; -import { FleetServerHostComboBox } from '../components'; +import { MultiRowInput } from '../../../sections/settings/components/multi_row_input'; export const getAddFleetServerHostStep = ({ fleetServerHostForm, @@ -49,27 +51,20 @@ export const AddFleetServerHostStepContent = ({ }: { fleetServerHostForm: FleetServerHostForm; }) => { - const { - fleetServerHost, - fleetServerHostSettings, - setFleetServerHost, - validateFleetServerHost, - saveFleetServerHost, - error, - } = fleetServerHostForm; + const { fleetServerHost, saveFleetServerHost, error, validate, inputs } = fleetServerHostForm; const [isLoading, setIsLoading] = useState(false); - const [submittedFleetServerHost, setSubmittedFleetServerHost] = useState(); + const [submittedFleetServerHost, setSubmittedFleetServerHost] = useState(); const { notifications } = useStartServices(); const { getHref } = useLink(); const onSubmit = useCallback(async () => { try { - setSubmittedFleetServerHost(''); + setSubmittedFleetServerHost(undefined); setIsLoading(true); - if (validateFleetServerHost()) { - await saveFleetServerHost(); + if (fleetServerHost && validate()) { + await saveFleetServerHost(fleetServerHost); setSubmittedFleetServerHost(fleetServerHost); } } catch (err) { @@ -81,18 +76,7 @@ export const AddFleetServerHostStepContent = ({ } finally { setIsLoading(false); } - }, [validateFleetServerHost, saveFleetServerHost, fleetServerHost, notifications.toasts]); - - const onChange = useCallback( - (host: string) => { - setFleetServerHost(host); - - if (error) { - validateFleetServerHost(); - } - }, - [error, setFleetServerHost, validateFleetServerHost] - ); + }, [validate, saveFleetServerHost, fleetServerHost, notifications.toasts]); return ( @@ -104,34 +88,58 @@ export const AddFleetServerHostStepContent = ({ /> - - - + } + {...inputs.nameInput.formRowProps} + > + + + + } + > + <> + {error && {error}} - - - - - - - + + + + + + {submittedFleetServerHost && ( <> @@ -150,7 +158,7 @@ export const AddFleetServerHostStepContent = ({ id="xpack.fleet.fleetServerSetup.addFleetServerHostSuccessText" defaultMessage="Added {host}. You can edit your Fleet Server hosts in {fleetSettingsLink}." values={{ - host: submittedFleetServerHost, + host: submittedFleetServerHost.host_urls[0], fleetSettingsLink: ( = - {/* - - - - {status === 'error' && {error}} - - */} } {...inputs.nameInput.formRowProps} > Date: Tue, 25 Oct 2022 06:38:44 -0400 Subject: [PATCH 07/16] Fix tests --- .../agent_policies/full_agent_policy.test.ts | 2 +- .../agent_policies/full_agent_policy.ts | 17 +++++++++++++---- .../fleet/server/services/fleet_server_host.ts | 8 +++++--- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts index ca2c910a43f5b..4ce84ea96eca3 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts @@ -39,7 +39,7 @@ function mockAgentPolicy(data: Partial) { jest.mock('../fleet_server_host', () => { return { - getFleetServerHostsForAgentPolicy: () => { + getFleetServerHostsForAgentPolicy: async () => { return { id: '93f74c0-e876-11ea-b7d3-8b2acec6f75c', is_default: true, diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts index d5f535195f497..3a1ba1d33abc9 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -20,6 +20,7 @@ import { getSourceUriForAgentPolicy } from '../../routes/agent/source_uri_utils' import { getPackageInfo } from '../epm/packages'; import { pkgToPkgKey, splitPkgKey } from '../epm/registry'; import { getFleetServerHostsForAgentPolicy } from '../fleet_server_host'; +import { appContextService } from '../app_context'; import { getMonitoringPermissions } from './monitoring_permissions'; import { storedPackagePoliciesToAgentInputs } from '.'; @@ -184,11 +185,19 @@ export async function getFullAgentPolicy( // only add fleet server hosts if not in standalone if (!standalone) { - const fleetServerHost = await getFleetServerHostsForAgentPolicy(soClient, agentPolicy); + const fleetServerHost = await getFleetServerHostsForAgentPolicy(soClient, agentPolicy).catch( + (err) => { + appContextService.getLogger()?.error(err); - fullAgentPolicy.fleet = { - hosts: fleetServerHost.host_urls, - }; + return; + } + ); + + if (fleetServerHost) { + fullAgentPolicy.fleet = { + hosts: fleetServerHost.host_urls, + }; + } } return fullAgentPolicy; } diff --git a/x-pack/plugins/fleet/server/services/fleet_server_host.ts b/x-pack/plugins/fleet/server/services/fleet_server_host.ts index ff870374e6d16..e63ddf1d905bd 100644 --- a/x-pack/plugins/fleet/server/services/fleet_server_host.ts +++ b/x-pack/plugins/fleet/server/services/fleet_server_host.ts @@ -182,14 +182,16 @@ export async function getFleetServerHostsForAgentPolicy( soClient: SavedObjectsClientContract, agentPolicy: AgentPolicy ) { + if (agentPolicy.fleet_server_host_id) { + return getFleetServerHost(soClient, agentPolicy.fleet_server_host_id); + } + const defaultFleetServerHost = await getDefaultFleetServerHost(soClient); if (!defaultFleetServerHost) { throw new Error('Default Fleet Server host is not setup'); } - const fleetServerHostId = agentPolicy.fleet_server_host_id || defaultFleetServerHost.id; - const fleetServerHost = await getFleetServerHost(soClient, fleetServerHostId); - return fleetServerHost; + return defaultFleetServerHost; } /** From 025d217053c2b9082ac364db7534d5c43fa8023a Mon Sep 17 00:00:00 2001 From: criamico Date: Tue, 25 Oct 2022 14:36:54 +0200 Subject: [PATCH 08/16] Fix checks and translations --- .../steps/add_fleet_server_host.tsx | 22 +++++++---------- .../steps/get_started.tsx | 24 +++++++------------ .../translations/translations/fr-FR.json | 4 ---- .../translations/translations/ja-JP.json | 4 ---- .../translations/translations/zh-CN.json | 4 ---- 5 files changed, 17 insertions(+), 41 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx index d651a6d084cd7..7391eb45bf95e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx @@ -92,39 +92,33 @@ export const AddFleetServerHostStepContent = ({ fullWidth label={ } {...inputs.nameInput.formRowProps} > + } > <> = <> 8220 }} /> @@ -98,39 +98,33 @@ const GettingStartedStepContent: React.FunctionComponent = fullWidth label={ } {...inputs.nameInput.formRowProps} > + } > <> Date: Tue, 25 Oct 2022 09:20:07 -0400 Subject: [PATCH 09/16] Fix socket hangup error --- x-pack/plugins/fleet/server/services/agent_policy.ts | 3 +-- x-pack/plugins/fleet/server/services/setup.ts | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index a3d54d9659179..b0fb4fbd4501e 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -683,9 +683,8 @@ class AgentPolicyService { // Use internal ES client so we have permissions to write to .fleet* indices const esClient = appContextService.getInternalUserESClient(); const defaultOutputId = await outputService.getDefaultDataOutputId(soClient); - const defaultFleetServerHost = await getDefaultFleetServerHost(soClient); - if (!defaultOutputId || !defaultFleetServerHost) { + if (!defaultOutputId) { return; } diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index 37f368a4b8647..1f39062bf9393 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -83,12 +83,13 @@ async function createSetupSideEffects( logger.debug('Setting up Fleet download source'); const defaultDownloadSource = await downloadSourceService.ensureDefault(soClient); - logger.debug('Setting up Fleet outputs'); - + logger.debug('Setting up Fleet Sever Hosts'); await ensurePreconfiguredFleetServerHosts( soClient, getPreconfiguredFleetServerHostFromConfig(appContextService.getConfig()) ); + + logger.debug('Setting up Fleet outputs'); await Promise.all([ ensurePreconfiguredOutputs( soClient, From b308e89d40458ea6206b4ae9042e3941f272a6dc Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 25 Oct 2022 13:30:15 +0000 Subject: [PATCH 10/16] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- x-pack/plugins/fleet/server/services/agent_policy.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index b0fb4fbd4501e..b9978d85ee73d 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -79,7 +79,6 @@ import { normalizeKuery, escapeSearchQueryPhrase } from './saved_object'; import { appContextService } from './app_context'; import { getFullAgentPolicy } from './agent_policies'; import { validateOutputForPolicy } from './agent_policies'; -import { getDefaultFleetServerHost } from './fleet_server_host'; const SAVED_OBJECT_TYPE = AGENT_POLICY_SAVED_OBJECT_TYPE; From 823c9176b44ada92847c02b8e6f1c56890d6c264 Mon Sep 17 00:00:00 2001 From: criamico Date: Wed, 26 Oct 2022 12:29:26 +0200 Subject: [PATCH 11/16] Fix cypress tests --- .../fleet/cypress/e2e/a11y/home_page.cy.ts | 9 ++-- .../fleet/cypress/e2e/fleet_settings.cy.ts | 44 +++++++++++++------ .../fleet/cypress/e2e/fleet_startup.cy.ts | 7 ++- x-pack/plugins/fleet/cypress/screens/fleet.ts | 17 ++++++- .../fleet/cypress/tasks/fleet_server.ts | 6 ++- .../steps/add_fleet_server_host.tsx | 25 ++++++++--- .../steps/get_started.tsx | 4 +- .../fleet_server_hosts_flyout/index.tsx | 1 + .../components/multi_row_input/index.tsx | 1 + .../fleet_server_hosts_section.tsx | 2 +- 10 files changed, 83 insertions(+), 33 deletions(-) diff --git a/x-pack/plugins/fleet/cypress/e2e/a11y/home_page.cy.ts b/x-pack/plugins/fleet/cypress/e2e/a11y/home_page.cy.ts index b76942ec9a456..6d1f714bda0d5 100644 --- a/x-pack/plugins/fleet/cypress/e2e/a11y/home_page.cy.ts +++ b/x-pack/plugins/fleet/cypress/e2e/a11y/home_page.cy.ts @@ -24,7 +24,7 @@ import { DATA_STREAMS_TAB, SETTINGS_TAB, SETTINGS_FLEET_SERVER_HOST_HEADING, - FLEET_SERVER_HOST_INPUT, + FLEET_SERVER_SETUP, } from '../../screens/fleet'; import { AGENT_POLICY_NAME_LINK } from '../../screens/integrations'; import { cleanupAgentPolicies, unenrollAgent } from '../../tasks/cleanup'; @@ -42,8 +42,9 @@ describe('Home page', () => { checkA11y({ skipFailures: false }); }); it('Install Fleet Server', () => { - cy.getBySel(FLEET_SERVER_HOST_INPUT, { timeout: 15000 }).should('be.visible'); - cy.getBySel(FLEET_SERVER_HOST_INPUT).getBySel('comboBoxSearchInput').type(fleetServerHost); + cy.getBySel(FLEET_SERVER_SETUP.NAME_INPUT).type('Host edited'); + cy.get('[placeholder="Specify host URL"', { timeout: 15000 }).should('be.visible'); + cy.get('[placeholder="Specify host URL"').type(fleetServerHost); cy.getBySel(GENERATE_FLEET_SERVER_POLICY_BUTTON).click(); cy.getBySel(PLATFORM_TYPE_LINUX_BUTTON, { timeout: 15000 }).should('be.visible'); checkA11y({ skipFailures: false }); @@ -58,6 +59,8 @@ describe('Home page', () => { checkA11y({ skipFailures: false }); }); it('Add your fleet sever host', () => { + cy.getBySel(FLEET_SERVER_SETUP.NAME_INPUT).type('New host'); + cy.get('[placeholder="Specify host URL"').type('https://localhost:8220'); cy.getBySel(ADVANCED_FLEET_SERVER_ADD_HOST_BUTTON).click(); checkA11y({ skipFailures: false }); }); diff --git a/x-pack/plugins/fleet/cypress/e2e/fleet_settings.cy.ts b/x-pack/plugins/fleet/cypress/e2e/fleet_settings.cy.ts index a1c4eef06bdb5..8e213532ce7b4 100644 --- a/x-pack/plugins/fleet/cypress/e2e/fleet_settings.cy.ts +++ b/x-pack/plugins/fleet/cypress/e2e/fleet_settings.cy.ts @@ -6,12 +6,27 @@ */ import { TOAST_CLOSE_BTN, CONFIRM_MODAL } from '../screens/navigation'; -import { SETTINGS_SAVE_BTN, SETTINGS_OUTPUTS } from '../screens/fleet'; +import { + SETTINGS_SAVE_BTN, + SETTINGS_OUTPUTS, + SETTINGS_FLEET_SERVER_HOSTS, + FLEET_SERVER_HOST_FLYOUT, +} from '../screens/fleet'; describe('Edit settings', () => { beforeEach(() => { - cy.intercept('/api/fleet/settings', { - item: { id: 'fleet-default-settings', fleet_server_hosts: [] }, + cy.intercept('/api/fleet/fleet_server_hosts', { + items: [ + { + id: 'fleet-default-settings', + name: 'Host', + host_urls: ['https://localhost:8220'], + is_default: true, + }, + ], + page: 1, + perPage: 10000, + total: 0, }); cy.intercept('/api/fleet/outputs', { items: [ @@ -29,22 +44,23 @@ describe('Edit settings', () => { cy.getBySel(TOAST_CLOSE_BTN).click(); }); - it('should update Fleet server hosts', () => { - cy.getBySel(SETTINGS_OUTPUTS.EDIT_HOSTS_BTN).click(); - cy.get('[placeholder="Specify host URL"').type('https://localhost:8220'); + it('should allow to update Fleet server hosts', () => { + cy.getBySel(SETTINGS_FLEET_SERVER_HOSTS.ADD_BUTTON).click(); + cy.getBySel(FLEET_SERVER_HOST_FLYOUT.NAME_INPUT).type('Host edited'); + cy.getBySel(FLEET_SERVER_HOST_FLYOUT.DEFAULT_SWITCH).click(); + cy.get('[placeholder="Specify host URL"').type('https://localhost:8221'); - cy.intercept('/api/fleet/settings', { - item: { id: 'fleet-default-settings', fleet_server_hosts: ['https://localhost:8220'] }, - }); - cy.intercept('PUT', '/api/fleet/settings', { - fleet_server_hosts: ['https://localhost:8220'], - }).as('updateSettings'); + cy.intercept('POST', '/api/fleet/fleet_server_hosts', { + name: 'Host edited', + host_urls: ['https://localhost:8221'], + is_default: true, + }).as('updateFleetServerHosts'); cy.getBySel(SETTINGS_SAVE_BTN).click(); cy.getBySel(CONFIRM_MODAL.CONFIRM_BUTTON).click(); - cy.wait('@updateSettings').then((interception) => { - expect(interception.request.body.fleet_server_hosts[0]).to.equal('https://localhost:8220'); + cy.wait('@updateFleetServerHosts').then((interception) => { + expect(interception.request.body.host_urls[0]).to.equal('https://localhost:8221'); }); }); diff --git a/x-pack/plugins/fleet/cypress/e2e/fleet_startup.cy.ts b/x-pack/plugins/fleet/cypress/e2e/fleet_startup.cy.ts index befa2074ac865..d79793d10d6bb 100644 --- a/x-pack/plugins/fleet/cypress/e2e/fleet_startup.cy.ts +++ b/x-pack/plugins/fleet/cypress/e2e/fleet_startup.cy.ts @@ -11,9 +11,9 @@ import { AGENT_FLYOUT, CREATE_FLEET_SERVER_POLICY_BTN, AGENT_POLICY_CREATE_STATUS_CALLOUT, - FLEET_SERVER_HOST_INPUT, ADVANCED_FLEET_SERVER_ADD_HOST_BUTTON, ADVANCED_FLEET_SERVER_GENERATE_SERVICE_TOKEN_BUTTON, + FLEET_SERVER_SETUP, } from '../screens/fleet'; import { cleanupAgentPolicies, unenrollAgent } from '../tasks/cleanup'; import { verifyPolicy, verifyAgentPackage, navigateToTab } from '../tasks/fleet'; @@ -98,9 +98,8 @@ describe('Fleet startup', () => { cy.getBySel(AGENT_FLYOUT.POLICY_DROPDOWN); // verify fleet server enroll command contains created policy id - cy.getBySel(FLEET_SERVER_HOST_INPUT) - .getBySel('comboBoxSearchInput') - .type('https://localhost:8220'); + cy.getBySel(FLEET_SERVER_SETUP.NAME_INPUT).type('New host'); + cy.get('[placeholder="Specify host URL"').type('https://localhost:8220'); cy.getBySel(ADVANCED_FLEET_SERVER_ADD_HOST_BUTTON).click(); cy.getBySel(ADVANCED_FLEET_SERVER_GENERATE_SERVICE_TOKEN_BUTTON).click(); diff --git a/x-pack/plugins/fleet/cypress/screens/fleet.ts b/x-pack/plugins/fleet/cypress/screens/fleet.ts index a9df1dc4d8ef1..ff54506f803f2 100644 --- a/x-pack/plugins/fleet/cypress/screens/fleet.ts +++ b/x-pack/plugins/fleet/cypress/screens/fleet.ts @@ -48,7 +48,7 @@ export const SETTINGS_SAVE_BTN = 'saveApplySettingsBtn'; export const AGENT_POLICY_SYSTEM_MONITORING_CHECKBOX = 'agentPolicyFormSystemMonitoringCheckbox'; export const INSTALL_INTEGRATIONS_ADVANCE_OPTIONS_BTN = 'AgentPolicyAdvancedOptions.AccordionBtn'; export const AGENT_POLICY_CREATE_STATUS_CALLOUT = 'agentPolicyCreateStatusCallOut'; -export const FLEET_SERVER_HOST_INPUT = 'fleetServerHostInput'; +// export const FLEET_SERVER_HOST_INPUT = 'fleetServerHostInput'; export const EXISTING_HOSTS_TAB = 'existingHostsTab'; export const NEW_HOSTS_TAB = 'newHostsTab'; @@ -96,11 +96,14 @@ export const AGENT_BINARY_SOURCES_FLYOUT = { export const SETTINGS_OUTPUTS = { EDIT_BTN: 'editOutputBtn', ADD_BTN: 'addOutputBtn', - EDIT_HOSTS_BTN: 'editHostsBtn', NAME_INPUT: 'settingsOutputsFlyout.nameInput', TYPE_INPUT: 'settingsOutputsFlyout.typeInput', }; +export const SETTINGS_FLEET_SERVER_HOSTS = { + ADD_BUTTON: 'settings.fleetServerHosts.addFleetServerHostBtn', +}; + export const AGENT_POLICY_FORM = { DOWNLOAD_SOURCE_SELECT: 'agentPolicyForm.downloadSource.select', }; @@ -114,3 +117,13 @@ export const FLEET_AGENT_LIST_PAGE = { CHECKBOX_SELECT_ALL: 'checkboxSelectAll', BULK_ACTIONS_BUTTON: 'agentBulkActionsButton', }; + +export const FLEET_SERVER_HOST_FLYOUT = { + NAME_INPUT: 'fleetServerHostsFlyout.nameInput', + DEFAULT_SWITCH: 'fleetServerHostsFlyout.isDefaultSwitch', +}; + +export const FLEET_SERVER_SETUP = { + NAME_INPUT: 'fleetServerSetup.nameInput', + HOST_INPUT: 'fleetServerSetup.multiRowInput', +}; diff --git a/x-pack/plugins/fleet/cypress/tasks/fleet_server.ts b/x-pack/plugins/fleet/cypress/tasks/fleet_server.ts index 946ded57e738f..f62b02ac3f784 100644 --- a/x-pack/plugins/fleet/cypress/tasks/fleet_server.ts +++ b/x-pack/plugins/fleet/cypress/tasks/fleet_server.ts @@ -52,10 +52,12 @@ export function setupFleetServer() { export function setFleetServerHost(host = 'https://fleetserver:8220') { cy.request({ method: 'PUT', - url: '/api/fleet/settings', + url: '/api/fleet/fleet_server_hosts', headers: { 'kbn-xsrf': 'xx' }, body: { - fleet_server_hosts: [host], + name: 'Default host', + host_urls: [host], + is_default: true, }, }); } diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx index 7391eb45bf95e..b4f4a6d9e0826 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx @@ -51,7 +51,7 @@ export const AddFleetServerHostStepContent = ({ }: { fleetServerHostForm: FleetServerHostForm; }) => { - const { fleetServerHost, saveFleetServerHost, error, validate, inputs } = fleetServerHostForm; + const { setFleetServerHost, saveFleetServerHost, error, validate, inputs } = fleetServerHostForm; const [isLoading, setIsLoading] = useState(false); const [submittedFleetServerHost, setSubmittedFleetServerHost] = useState(); @@ -63,9 +63,17 @@ export const AddFleetServerHostStepContent = ({ setSubmittedFleetServerHost(undefined); setIsLoading(true); - if (fleetServerHost && validate()) { - await saveFleetServerHost(fleetServerHost); - setSubmittedFleetServerHost(fleetServerHost); + const newFleetServerHost = { + name: inputs.nameInput.value, + host_urls: inputs.hostUrlsInput.value, + is_default: true, + id: 'fleet-server-host', + is_preconfigured: false, + }; + setFleetServerHost(newFleetServerHost); + if (validate()) { + await saveFleetServerHost(newFleetServerHost); + setSubmittedFleetServerHost(newFleetServerHost); } } catch (err) { notifications.toasts.addError(err, { @@ -76,7 +84,14 @@ export const AddFleetServerHostStepContent = ({ } finally { setIsLoading(false); } - }, [validate, saveFleetServerHost, fleetServerHost, notifications.toasts]); + }, [ + inputs.nameInput.value, + inputs.hostUrlsInput.value, + setFleetServerHost, + validate, + saveFleetServerHost, + notifications.toasts, + ]); return ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/get_started.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/get_started.tsx index db733c5384b59..07a34be899796 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/get_started.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/get_started.tsx @@ -105,7 +105,7 @@ const GettingStartedStepContent: React.FunctionComponent = {...inputs.nameInput.formRowProps} > = > <> = ({ {displayErrors(globalErrors)} Date: Wed, 26 Oct 2022 15:11:34 +0200 Subject: [PATCH 12/16] Fix API request in cypress test --- x-pack/plugins/fleet/cypress/tasks/fleet_server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/cypress/tasks/fleet_server.ts b/x-pack/plugins/fleet/cypress/tasks/fleet_server.ts index f62b02ac3f784..4a75d535c12f2 100644 --- a/x-pack/plugins/fleet/cypress/tasks/fleet_server.ts +++ b/x-pack/plugins/fleet/cypress/tasks/fleet_server.ts @@ -51,7 +51,7 @@ export function setupFleetServer() { export function setFleetServerHost(host = 'https://fleetserver:8220') { cy.request({ - method: 'PUT', + method: 'POST', url: '/api/fleet/fleet_server_hosts', headers: { 'kbn-xsrf': 'xx' }, body: { From 4e5b01a07cf89f77bd51fa52c60de74837562804 Mon Sep 17 00:00:00 2001 From: criamico Date: Thu, 27 Oct 2022 12:47:28 +0200 Subject: [PATCH 13/16] Fix last failing test --- x-pack/plugins/fleet/cypress/e2e/fleet_agent_flyout.cy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/fleet/cypress/e2e/fleet_agent_flyout.cy.ts b/x-pack/plugins/fleet/cypress/e2e/fleet_agent_flyout.cy.ts index b907fb8ef4c79..9f566cde15bff 100644 --- a/x-pack/plugins/fleet/cypress/e2e/fleet_agent_flyout.cy.ts +++ b/x-pack/plugins/fleet/cypress/e2e/fleet_agent_flyout.cy.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ADD_AGENT_BUTTON, AGENT_FLYOUT } from '../screens/fleet'; +import { ADD_AGENT_BUTTON_TOP, AGENT_FLYOUT } from '../screens/fleet'; import { cleanupAgentPolicies, deleteFleetServerDocs, deleteAgentDocs } from '../tasks/cleanup'; import { createAgentDoc } from '../tasks/agents'; import { setFleetServerHost } from '../tasks/fleet_server'; @@ -69,7 +69,7 @@ describe('Fleet add agent flyout', () => { const AGENT_ID = 'agent' + Date.now(); navigateTo(FLEET); - cy.getBySel(ADD_AGENT_BUTTON).click(); + cy.getBySel(ADD_AGENT_BUTTON_TOP).click(); cy.intercept('POST', '/api/fleet/agent_policies?sys_monitoring=true').as('createAgentPolicy'); cy.getBySel(AGENT_FLYOUT.CREATE_POLICY_BUTTON).click(); From d00c606c57e80448d1e536210b3c529cbccdfab4 Mon Sep 17 00:00:00 2001 From: criamico Date: Thu, 27 Oct 2022 15:58:42 +0200 Subject: [PATCH 14/16] Add timeout --- x-pack/plugins/fleet/cypress/e2e/fleet_agent_flyout.cy.ts | 2 +- x-pack/plugins/fleet/cypress/screens/fleet.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/fleet/cypress/e2e/fleet_agent_flyout.cy.ts b/x-pack/plugins/fleet/cypress/e2e/fleet_agent_flyout.cy.ts index 9f566cde15bff..6e0c3f820a51a 100644 --- a/x-pack/plugins/fleet/cypress/e2e/fleet_agent_flyout.cy.ts +++ b/x-pack/plugins/fleet/cypress/e2e/fleet_agent_flyout.cy.ts @@ -69,7 +69,7 @@ describe('Fleet add agent flyout', () => { const AGENT_ID = 'agent' + Date.now(); navigateTo(FLEET); - cy.getBySel(ADD_AGENT_BUTTON_TOP).click(); + cy.getBySel(ADD_AGENT_BUTTON_TOP, { timeout: 150000 }).click(); cy.intercept('POST', '/api/fleet/agent_policies?sys_monitoring=true').as('createAgentPolicy'); cy.getBySel(AGENT_FLYOUT.CREATE_POLICY_BUTTON).click(); diff --git a/x-pack/plugins/fleet/cypress/screens/fleet.ts b/x-pack/plugins/fleet/cypress/screens/fleet.ts index ff54506f803f2..7bd8c6293e97d 100644 --- a/x-pack/plugins/fleet/cypress/screens/fleet.ts +++ b/x-pack/plugins/fleet/cypress/screens/fleet.ts @@ -48,7 +48,7 @@ export const SETTINGS_SAVE_BTN = 'saveApplySettingsBtn'; export const AGENT_POLICY_SYSTEM_MONITORING_CHECKBOX = 'agentPolicyFormSystemMonitoringCheckbox'; export const INSTALL_INTEGRATIONS_ADVANCE_OPTIONS_BTN = 'AgentPolicyAdvancedOptions.AccordionBtn'; export const AGENT_POLICY_CREATE_STATUS_CALLOUT = 'agentPolicyCreateStatusCallOut'; -// export const FLEET_SERVER_HOST_INPUT = 'fleetServerHostInput'; + export const EXISTING_HOSTS_TAB = 'existingHostsTab'; export const NEW_HOSTS_TAB = 'newHostsTab'; From d2c3b1bbe9b2c084b75455d435364d0d72d2db4d Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Thu, 27 Oct 2022 15:21:00 -0400 Subject: [PATCH 15/16] Fix tests --- .../fleet/common/types/models/settings.ts | 2 +- .../cypress/e2e/fleet_agent_flyout.cy.ts | 4 +- .../agent_enrollment_flyout.test.mocks.ts | 11 +- .../agent_enrollment_flyout.test.tsx | 18 +- .../agent_enrollment_flyout/index.tsx | 13 +- .../agent_enrollment_flyout/instructions.tsx | 12 +- .../steps/compute_steps.tsx | 15 +- .../agent_enrollment_flyout/types.ts | 4 +- .../server/services/fleet_server_host.test.ts | 47 ---- .../fleet_server_host.test.ts | 52 +++- .../preconfiguration/fleet_server_host.ts | 32 +++ .../fleet/server/services/settings.test.ts | 245 +----------------- .../plugins/fleet/server/services/settings.ts | 47 +--- 13 files changed, 139 insertions(+), 363 deletions(-) diff --git a/x-pack/plugins/fleet/common/types/models/settings.ts b/x-pack/plugins/fleet/common/types/models/settings.ts index 17bbe7a73c1da..5a33fea910446 100644 --- a/x-pack/plugins/fleet/common/types/models/settings.ts +++ b/x-pack/plugins/fleet/common/types/models/settings.ts @@ -9,7 +9,7 @@ import type { SavedObjectAttributes } from '@kbn/core/public'; export interface BaseSettings { has_seen_add_data_notice?: boolean; - fleet_server_hosts: string[]; + fleet_server_hosts?: string[]; } export interface Settings extends BaseSettings { diff --git a/x-pack/plugins/fleet/cypress/e2e/fleet_agent_flyout.cy.ts b/x-pack/plugins/fleet/cypress/e2e/fleet_agent_flyout.cy.ts index 6e0c3f820a51a..b907fb8ef4c79 100644 --- a/x-pack/plugins/fleet/cypress/e2e/fleet_agent_flyout.cy.ts +++ b/x-pack/plugins/fleet/cypress/e2e/fleet_agent_flyout.cy.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ADD_AGENT_BUTTON_TOP, AGENT_FLYOUT } from '../screens/fleet'; +import { ADD_AGENT_BUTTON, AGENT_FLYOUT } from '../screens/fleet'; import { cleanupAgentPolicies, deleteFleetServerDocs, deleteAgentDocs } from '../tasks/cleanup'; import { createAgentDoc } from '../tasks/agents'; import { setFleetServerHost } from '../tasks/fleet_server'; @@ -69,7 +69,7 @@ describe('Fleet add agent flyout', () => { const AGENT_ID = 'agent' + Date.now(); navigateTo(FLEET); - cy.getBySel(ADD_AGENT_BUTTON_TOP, { timeout: 150000 }).click(); + cy.getBySel(ADD_AGENT_BUTTON).click(); cy.intercept('POST', '/api/fleet/agent_policies?sys_monitoring=true').as('createAgentPolicy'); cy.getBySel(AGENT_FLYOUT.CREATE_POLICY_BUTTON).click(); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts index 371edf0c6f6e9..e853e853aecb5 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts @@ -17,8 +17,15 @@ jest.mock('../../hooks/use_request', () => { const module = jest.requireActual('../../hooks/use_request'); return { ...module, - useGetSettings: jest.fn().mockReturnValue({ - data: { item: { fleet_server_hosts: ['test'] } }, + useGetFleetServerHosts: jest.fn().mockReturnValue({ + data: { + items: [ + { + is_default: true, + host_urls: ['http://test.fr'], + }, + ], + }, }), sendGetOneAgentPolicy: jest.fn().mockResolvedValue({ data: { item: { package_policies: [] } }, diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx index 6e46ec90d5faf..ea184084b040e 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx @@ -16,7 +16,11 @@ import { coreMock } from '@kbn/core/public/mocks'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import type { AgentPolicy } from '../../../common'; -import { useGetSettings, sendGetOneAgentPolicy, useGetAgents } from '../../hooks/use_request'; +import { + useGetFleetServerHosts, + sendGetOneAgentPolicy, + useGetAgents, +} from '../../hooks/use_request'; import { FleetStatusProvider, ConfigContext, @@ -78,8 +82,15 @@ describe('', () => { let testBed: TestBed; beforeEach(() => { - (useGetSettings as jest.Mock).mockReturnValue({ - data: { item: { fleet_server_hosts: ['test'] } }, + (useGetFleetServerHosts as jest.Mock).mockReturnValue({ + data: { + items: [ + { + is_default: true, + host_urls: ['http://test.fr'], + }, + ], + }, }); (useFleetStatus as jest.Mock).mockReturnValue({ isReady: true }); @@ -155,7 +166,6 @@ describe('', () => { describe('managed instructions', () => { it('uses the agent policy selection step', () => { const { exists } = testBed; - expect(exists('agentEnrollmentFlyout')).toBe(true); expect(exists('agent-policy-selection-step')).toBe(true); expect(exists('agent-enrollment-key-selection-step')).toBe(false); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx index cde975fed45dd..9cbd8f66fc246 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx @@ -22,7 +22,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useGetSettings, useFleetStatus, useAgentEnrollmentFlyoutData } from '../../hooks'; +import { useFleetStatus, useAgentEnrollmentFlyoutData, useGetFleetServerHosts } from '../../hooks'; import { FLEET_SERVER_PACKAGE } from '../../constants'; import type { PackagePolicy, AgentPolicy } from '../../types'; @@ -51,9 +51,11 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ return policies.find((p) => p.id === id); }; - const settings = useGetSettings(); + const fleetServerHostsRequest = useGetFleetServerHosts(); + const fleetStatus = useFleetStatus(); - const fleetServerHosts = settings.data?.item?.fleet_server_hosts || []; + const fleetServerHosts = + fleetServerHostsRequest.data?.items?.filter((f) => true)?.[0]?.host_urls ?? []; const [selectedPolicyId, setSelectedPolicyId] = useState(agentPolicy?.id); const [isFleetServerPolicySelected, setIsFleetServerPolicySelected] = useState(false); @@ -92,7 +94,8 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ const { isK8s } = useIsK8sPolicy(selectedPolicy ? selectedPolicy : undefined); - const isLoadingInitialRequest = settings.isLoading && settings.isInitialRequest; + const isLoadingInitialRequest = + fleetServerHostsRequest.isLoading && fleetServerHostsRequest.isInitialRequest; return ( @@ -151,7 +154,7 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ ) : ( { const { agentPolicies, isFleetServerPolicySelected, - settings, + fleetServerHosts, isLoadingAgentPolicies, selectionType, setSelectionType, @@ -66,19 +66,19 @@ export const Instructions = (props: InstructionProps) => { const fleetServers = agents?.items || []; - const fleetServerHosts = useMemo(() => { - return settings?.fleet_server_hosts || []; - }, [settings]); + const displayFleetServerHosts = useMemo(() => { + return fleetServerHosts?.filter((f) => f.is_default)?.[0]?.host_urls || []; + }, [fleetServerHosts]); if (isLoadingAgents || isLoadingAgentPolicies) return ; - const hasNoFleetServerHost = fleetStatus.isReady && fleetServerHosts.length === 0; + const hasNoFleetServerHost = fleetStatus.isReady && displayFleetServerHosts.length === 0; const showAgentEnrollment = fleetStatus.isReady && !isFleetServerUnhealthy && fleetServers.length > 0 && - fleetServerHosts.length > 0; + displayFleetServerHosts.length > 0; const showFleetServerEnrollment = fleetServers.length === 0 || diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx index 1bb7f446ec77c..4bb167fb2494e 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx @@ -186,7 +186,7 @@ export const ManagedSteps: React.FunctionComponent = ({ setSelectedPolicyId, selectedApiKeyId, setSelectedAPIKeyId, - settings, + fleetServerHosts, refreshAgentPolicies, mode, setMode, @@ -207,10 +207,15 @@ export const ManagedSteps: React.FunctionComponent = ({ const enrolledAgentIds = usePollingAgentCount(selectedPolicy?.id || ''); - const fleetServerHosts = useMemo(() => { - return settings?.fleet_server_hosts || []; - }, [settings]); - const installManagedCommands = ManualInstructions(enrollToken, fleetServerHosts, kibanaVersion); + const displayFleetServerHosts = useMemo(() => { + return fleetServerHosts?.filter((f) => f.is_default)?.[0]?.host_urls ?? []; + }, [fleetServerHosts]); + + const installManagedCommands = ManualInstructions( + enrollToken, + displayFleetServerHosts, + kibanaVersion + ); const instructionsSteps = useMemo(() => { const steps: EuiContainedStepProps[] = !agentPolicy diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts index 7c501df0b3f3b..7215cba9e417f 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { AgentPolicy, Settings } from '../../types'; +import type { AgentPolicy, FleetServerHost } from '../../types'; import type { InstalledIntegrationPolicy } from './use_get_agent_incoming_data'; @@ -19,7 +19,7 @@ export interface BaseProps { */ agentPolicy?: AgentPolicy; - settings?: Settings; + fleetServerHosts?: FleetServerHost[]; isFleetServerPolicySelected?: boolean; diff --git a/x-pack/plugins/fleet/server/services/fleet_server_host.test.ts b/x-pack/plugins/fleet/server/services/fleet_server_host.test.ts index 72602df6af3db..40f65ca21c5ef 100644 --- a/x-pack/plugins/fleet/server/services/fleet_server_host.test.ts +++ b/x-pack/plugins/fleet/server/services/fleet_server_host.test.ts @@ -13,54 +13,7 @@ import { DEFAULT_FLEET_SERVER_HOST_ID, } from '../constants'; -import { appContextService } from './app_context'; import { migrateSettingsToFleetServerHost } from './fleet_server_host'; -import { getCloudFleetServersHosts } from './settings'; - -jest.mock('./app_context'); - -const mockedAppContextService = appContextService as jest.Mocked; - -describe('getCloudFleetServersHosts', () => { - afterEach(() => { - mockedAppContextService.getCloud.mockReset(); - }); - it('should return undefined if cloud is not setup', () => { - expect(getCloudFleetServersHosts()).toBeUndefined(); - }); - - it('should return fleet server hosts if cloud is correctly setup with default port == 443', () => { - mockedAppContextService.getCloud.mockReturnValue({ - cloudId: - 'dXMtZWFzdC0xLmF3cy5mb3VuZC5pbyRjZWM2ZjI2MWE3NGJmMjRjZTMzYmI4ODExYjg0Mjk0ZiRjNmMyY2E2ZDA0MjI0OWFmMGNjN2Q3YTllOTYyNTc0Mw==', - isCloudEnabled: true, - deploymentId: 'deployment-id-1', - apm: {}, - }); - - expect(getCloudFleetServersHosts()).toMatchInlineSnapshot(` - Array [ - "https://deployment-id-1.fleet.us-east-1.aws.found.io", - ] - `); - }); - - it('should return fleet server hosts if cloud is correctly setup with a default port', () => { - mockedAppContextService.getCloud.mockReturnValue({ - cloudId: - 'test:dGVzdC5mcjo5MjQzJGRhM2I2YjNkYWY5ZDRjODE4ZjI4ZmEzNDdjMzgzODViJDgxMmY4NWMxZjNjZTQ2YTliYjgxZjFjMWIxMzRjNmRl', - isCloudEnabled: true, - deploymentId: 'deployment-id-1', - apm: {}, - }); - - expect(getCloudFleetServersHosts()).toMatchInlineSnapshot(` - Array [ - "https://deployment-id-1.fleet.test.fr:9243", - ] - `); - }); -}); describe('migrateSettingsToFleetServerHost', () => { it('should not migrate settings if a default fleet server policy config exists', async () => { diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.test.ts b/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.test.ts index 468058f87f448..685f12f21cb4d 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.test.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.test.ts @@ -5,9 +5,16 @@ * 2.0. */ -import { getPreconfiguredFleetServerHostFromConfig } from './fleet_server_host'; +import { appContextService } from '../app_context'; -jest.mock('../fleet_server_host'); +import { + getCloudFleetServersHosts, + getPreconfiguredFleetServerHostFromConfig, +} from './fleet_server_host'; + +jest.mock('../app_context'); + +const mockedAppContextService = appContextService as jest.Mocked; describe('getPreconfiguredFleetServerHostFromConfig', () => { it('should work with preconfigured fleetServerHosts', () => { @@ -81,3 +88,44 @@ describe('getPreconfiguredFleetServerHostFromConfig', () => { ); }); }); + +describe('getCloudFleetServersHosts', () => { + afterEach(() => { + mockedAppContextService.getCloud.mockReset(); + }); + it('should return undefined if cloud is not setup', () => { + expect(getCloudFleetServersHosts()).toBeUndefined(); + }); + + it('should return fleet server hosts if cloud is correctly setup with default port == 443', () => { + mockedAppContextService.getCloud.mockReturnValue({ + cloudId: + 'dXMtZWFzdC0xLmF3cy5mb3VuZC5pbyRjZWM2ZjI2MWE3NGJmMjRjZTMzYmI4ODExYjg0Mjk0ZiRjNmMyY2E2ZDA0MjI0OWFmMGNjN2Q3YTllOTYyNTc0Mw==', + isCloudEnabled: true, + deploymentId: 'deployment-id-1', + apm: {}, + }); + + expect(getCloudFleetServersHosts()).toMatchInlineSnapshot(` + Array [ + "https://deployment-id-1.fleet.us-east-1.aws.found.io", + ] + `); + }); + + it('should return fleet server hosts if cloud is correctly setup with a default port', () => { + mockedAppContextService.getCloud.mockReturnValue({ + cloudId: + 'test:dGVzdC5mcjo5MjQzJGRhM2I2YjNkYWY5ZDRjODE4ZjI4ZmEzNDdjMzgzODViJDgxMmY4NWMxZjNjZTQ2YTliYjgxZjFjMWIxMzRjNmRl', + isCloudEnabled: true, + deploymentId: 'deployment-id-1', + apm: {}, + }); + + expect(getCloudFleetServersHosts()).toMatchInlineSnapshot(` + Array [ + "https://deployment-id-1.fleet.test.fr:9243", + ] + `); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.ts b/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.ts index 465a2f8706ea9..6f3a67cf0c2f2 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.ts @@ -8,10 +8,12 @@ import type { SavedObjectsClientContract } from '@kbn/core/server'; import { isEqual } from 'lodash'; +import { decodeCloudId } from '../../../common/services'; import type { FleetConfigType } from '../../config'; import { DEFAULT_FLEET_SERVER_HOST_ID } from '../../constants'; import type { FleetServerHost } from '../../types'; +import { appContextService } from '../app_context'; import { bulkGetFleetServerHosts, createFleetServerHost, @@ -20,12 +22,42 @@ import { updateFleetServerHost, } from '../fleet_server_host'; +export function getCloudFleetServersHosts() { + const cloudSetup = appContextService.getCloud(); + if (cloudSetup && cloudSetup.isCloudEnabled && cloudSetup.cloudId && cloudSetup.deploymentId) { + const res = decodeCloudId(cloudSetup.cloudId); + if (!res) { + return; + } + + // Fleet Server url are formed like this `https://.fleet. + return [ + `https://${cloudSetup.deploymentId}.fleet.${res.host}${ + res.defaultPort !== '443' ? `:${res.defaultPort}` : '' + }`, + ]; + } +} + export function getPreconfiguredFleetServerHostFromConfig(config?: FleetConfigType) { const { fleetServerHosts: fleetServerHostsFromConfig } = config; const legacyFleetServerHostsConfig = getConfigFleetServerHosts(config); + // is cloud + const cloudServerHosts = getCloudFleetServersHosts(); + const fleetServerHosts: FleetServerHost[] = (fleetServerHostsFromConfig || []).concat([ + ...(cloudServerHosts + ? [ + { + name: 'Default', + is_default: true, + id: DEFAULT_FLEET_SERVER_HOST_ID, + host_urls: cloudServerHosts, + }, + ] + : []), ...(legacyFleetServerHostsConfig ? [ { diff --git a/x-pack/plugins/fleet/server/services/settings.test.ts b/x-pack/plugins/fleet/server/services/settings.test.ts index 642553f0db674..ca0be8130b21a 100644 --- a/x-pack/plugins/fleet/server/services/settings.test.ts +++ b/x-pack/plugins/fleet/server/services/settings.test.ts @@ -8,53 +8,12 @@ import { savedObjectsClientMock } from '@kbn/core/server/mocks'; import { appContextService } from './app_context'; -import { getCloudFleetServersHosts, settingsSetup } from './settings'; +import { settingsSetup } from './settings'; jest.mock('./app_context'); const mockedAppContextService = appContextService as jest.Mocked; -describe('getCloudFleetServersHosts', () => { - afterEach(() => { - mockedAppContextService.getCloud.mockReset(); - }); - it('should return undefined if cloud is not setup', () => { - expect(getCloudFleetServersHosts()).toBeUndefined(); - }); - - it('should return fleet server hosts if cloud is correctly setup with default port == 443', () => { - mockedAppContextService.getCloud.mockReturnValue({ - cloudId: - 'dXMtZWFzdC0xLmF3cy5mb3VuZC5pbyRjZWM2ZjI2MWE3NGJmMjRjZTMzYmI4ODExYjg0Mjk0ZiRjNmMyY2E2ZDA0MjI0OWFmMGNjN2Q3YTllOTYyNTc0Mw==', - isCloudEnabled: true, - deploymentId: 'deployment-id-1', - apm: {}, - }); - - expect(getCloudFleetServersHosts()).toMatchInlineSnapshot(` - Array [ - "https://deployment-id-1.fleet.us-east-1.aws.found.io", - ] - `); - }); - - it('should return fleet server hosts if cloud is correctly setup with a default port', () => { - mockedAppContextService.getCloud.mockReturnValue({ - cloudId: - 'test:dGVzdC5mcjo5MjQzJGRhM2I2YjNkYWY5ZDRjODE4ZjI4ZmEzNDdjMzgzODViJDgxMmY4NWMxZjNjZTQ2YTliYjgxZjFjMWIxMzRjNmRl', - isCloudEnabled: true, - deploymentId: 'deployment-id-1', - apm: {}, - }); - - expect(getCloudFleetServersHosts()).toMatchInlineSnapshot(` - Array [ - "https://deployment-id-1.fleet.test.fr:9243", - ] - `); - }); -}); - describe('settingsSetup', () => { afterEach(() => { mockedAppContextService.getCloud.mockReset(); @@ -82,7 +41,7 @@ describe('settingsSetup', () => { expect(soClientMock.create).toBeCalled(); }); - it('should do nothing if there is settings and no default fleet server hosts', async () => { + it('should do nothing if there is settings', async () => { const soClientMock = savedObjectsClientMock.create(); soClientMock.find.mockResolvedValue({ @@ -111,204 +70,4 @@ describe('settingsSetup', () => { expect(soClientMock.create).not.toBeCalled(); }); - - it('should update settings if there is settings without fleet server hosts and default fleet server hosts', async () => { - const soClientMock = savedObjectsClientMock.create(); - mockedAppContextService.getCloud.mockReturnValue({ - cloudId: - 'test:dGVzdC5mcjo5MjQzJGRhM2I2YjNkYWY5ZDRjODE4ZjI4ZmEzNDdjMzgzODViJDgxMmY4NWMxZjNjZTQ2YTliYjgxZjFjMWIxMzRjNmRl', - isCloudEnabled: true, - deploymentId: 'deployment-id-1', - apm: {}, - }); - - soClientMock.find.mockResolvedValue({ - total: 1, - page: 0, - per_page: 10, - saved_objects: [ - { - id: 'defaultsettings', - attributes: {}, - type: 'so_type', - references: [], - score: 0, - }, - ], - }); - - soClientMock.update.mockResolvedValue({ - id: 'updated', - attributes: {}, - references: [], - type: 'so_type', - }); - - soClientMock.create.mockResolvedValue({ - id: 'created', - attributes: {}, - references: [], - type: 'so_type', - }); - - await settingsSetup(soClientMock); - - expect(soClientMock.create).not.toBeCalled(); - expect(soClientMock.update).toBeCalledWith('ingest_manager_settings', 'defaultsettings', { - fleet_server_hosts: ['https://deployment-id-1.fleet.test.fr:9243'], - }); - }); - - it('should update settings if there is a new fleet server host in the config', async () => { - const soClientMock = savedObjectsClientMock.create(); - mockedAppContextService.getCloud.mockReturnValue({ - cloudId: - 'test:dGVzdC5mcjo5MjQzJGRhM2I2YjNkYWY5ZDRjODE4ZjI4ZmEzNDdjMzgzODViJDgxMmY4NWMxZjNjZTQ2YTliYjgxZjFjMWIxMzRjNmRl', - isCloudEnabled: true, - deploymentId: 'deployment-id-1', - apm: {}, - }); - mockedAppContextService.getConfig.mockReturnValue({ - agents: { - fleet_server: { hosts: ['http://fleetserverupdated.fr:8220'] }, - }, - } as any); - - soClientMock.find.mockResolvedValue({ - total: 1, - page: 0, - per_page: 10, - saved_objects: [ - { - id: 'defaultsettings', - attributes: { - fleet_server_hosts: ['https://deployment-id-1.fleet.test.fr:9243'], - }, - type: 'so_type', - references: [], - score: 0, - }, - ], - }); - - soClientMock.update.mockResolvedValue({ - id: 'updated', - attributes: {}, - references: [], - type: 'so_type', - }); - - soClientMock.create.mockResolvedValue({ - id: 'created', - attributes: {}, - references: [], - type: 'so_type', - }); - - await settingsSetup(soClientMock); - - expect(soClientMock.create).not.toBeCalled(); - expect(soClientMock.update).toBeCalledWith('ingest_manager_settings', 'defaultsettings', { - fleet_server_hosts: ['http://fleetserverupdated.fr:8220'], - }); - }); - - it('should update settings if there is no new fleet server hosts in the config', async () => { - const soClientMock = savedObjectsClientMock.create(); - mockedAppContextService.getCloud.mockReturnValue({ - cloudId: - 'test:dGVzdC5mcjo5MjQzJGRhM2I2YjNkYWY5ZDRjODE4ZjI4ZmEzNDdjMzgzODViJDgxMmY4NWMxZjNjZTQ2YTliYjgxZjFjMWIxMzRjNmRl', - isCloudEnabled: true, - deploymentId: 'deployment-id-1', - apm: {}, - }); - mockedAppContextService.getConfig.mockReturnValue({ - agents: { - fleet_server: { hosts: ['http://fleetserverupdated.fr:8220'] }, - }, - } as any); - - soClientMock.find.mockResolvedValue({ - total: 1, - page: 0, - per_page: 10, - saved_objects: [ - { - id: 'defaultsettings', - attributes: { - fleet_server_hosts: ['http://fleetserverupdated.fr:8220'], - }, - type: 'so_type', - references: [], - score: 0, - }, - ], - }); - - soClientMock.update.mockResolvedValue({ - id: 'updated', - attributes: {}, - references: [], - type: 'so_type', - }); - - soClientMock.create.mockResolvedValue({ - id: 'created', - attributes: {}, - references: [], - type: 'so_type', - }); - - await settingsSetup(soClientMock); - - expect(soClientMock.create).not.toBeCalled(); - expect(soClientMock.update).not.toBeCalled(); - }); - - it('should not update settings with cloud settings if there is settings with fleet server hosts and default fleet server hosts', async () => { - const soClientMock = savedObjectsClientMock.create(); - mockedAppContextService.getCloud.mockReturnValue({ - cloudId: - 'test:dGVzdC5mcjo5MjQzJGRhM2I2YjNkYWY5ZDRjODE4ZjI4ZmEzNDdjMzgzODViJDgxMmY4NWMxZjNjZTQ2YTliYjgxZjFjMWIxMzRjNmRl', - isCloudEnabled: true, - deploymentId: 'deployment-id-1', - apm: {}, - }); - - soClientMock.find.mockResolvedValue({ - total: 1, - page: 0, - per_page: 10, - saved_objects: [ - { - id: 'defaultsettings', - attributes: { - fleet_server_hosts: ['http://fleetserver:1234'], - }, - type: 'so_type', - references: [], - score: 0, - }, - ], - }); - - soClientMock.update.mockResolvedValue({ - id: 'updated', - attributes: {}, - references: [], - type: 'so_type', - }); - - soClientMock.create.mockResolvedValue({ - id: 'created', - attributes: {}, - references: [], - type: 'so_type', - }); - - await settingsSetup(soClientMock); - - expect(soClientMock.create).not.toBeCalled(); - expect(soClientMock.update).not.toBeCalled(); - }); }); diff --git a/x-pack/plugins/fleet/server/services/settings.ts b/x-pack/plugins/fleet/server/services/settings.ts index 41b30acc512eb..5cde2dbf99815 100644 --- a/x-pack/plugins/fleet/server/services/settings.ts +++ b/x-pack/plugins/fleet/server/services/settings.ts @@ -6,10 +6,9 @@ */ import Boom from '@hapi/boom'; -import { isEqual } from 'lodash'; import type { SavedObjectsClientContract } from '@kbn/core/server'; -import { decodeCloudId, normalizeHostsForAgents } from '../../common/services'; +import { normalizeHostsForAgents } from '../../common/services'; import { GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, GLOBAL_SETTINGS_ID } from '../../common/constants'; import type { SettingsSOAttributes, Settings, BaseSettings } from '../../common/types'; @@ -34,23 +33,7 @@ export async function getSettings(soClient: SavedObjectsClientContract): Promise export async function settingsSetup(soClient: SavedObjectsClientContract) { try { - const settings = await getSettings(soClient); - const defaultSettings = createDefaultSettings(); - - const fleetServerHostsIsPreconfigured = getConfigFleetServerHosts()?.length ?? 0 > 0; - - const fleetServerHostsShouldBeUpdated = - !settings.fleet_server_hosts || - settings.fleet_server_hosts.length === 0 || - (fleetServerHostsIsPreconfigured && - !isEqual(settings.fleet_server_hosts, defaultSettings.fleet_server_hosts)); - - // Migration for < 7.13 Kibana - if (defaultSettings.fleet_server_hosts.length > 0 && fleetServerHostsShouldBeUpdated) { - return saveSettings(soClient, { - fleet_server_hosts: defaultSettings.fleet_server_hosts, - }); - } + await getSettings(soClient); } catch (e) { if (e.isBoom && e.output.statusCode === 404) { const defaultSettings = createDefaultSettings(); @@ -116,29 +99,5 @@ function getConfigFleetServerHosts() { } export function createDefaultSettings(): BaseSettings { - const configFleetServerHosts = getConfigFleetServerHosts(); - const cloudFleetServerHosts = getCloudFleetServersHosts(); - - const fleetServerHosts = configFleetServerHosts ?? cloudFleetServerHosts ?? []; - - return { - fleet_server_hosts: fleetServerHosts, - }; -} - -export function getCloudFleetServersHosts() { - const cloudSetup = appContextService.getCloud(); - if (cloudSetup && cloudSetup.isCloudEnabled && cloudSetup.cloudId && cloudSetup.deploymentId) { - const res = decodeCloudId(cloudSetup.cloudId); - if (!res) { - return; - } - - // Fleet Server url are formed like this `https://.fleet. - return [ - `https://${cloudSetup.deploymentId}.fleet.${res.host}${ - res.defaultPort !== '443' ? `:${res.defaultPort}` : '' - }`, - ]; - } + return {}; } From c177dfb9dcf016fe3d86981ac2b3bc921cc994f0 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Thu, 27 Oct 2022 16:37:31 -0400 Subject: [PATCH 16/16] Fix tests --- .../install_agent/install_agent_managed.tsx | 8 ++------ .../multi_page_layout/index.tsx | 15 +++++++++------ .../multi_page_layout/types.ts | 3 +-- .../fleet/sections/agents/index.test.tsx | 8 -------- .../use_fleet_server_host_form.tsx | 6 +++++- .../settings_page/fleet_server_hosts_section.tsx | 4 +--- .../settings/components/settings_page/index.tsx | 5 +---- .../fleet/sections/settings/index.tsx | 15 +-------------- .../agent_enrollment_flyout.test.mocks.ts | 3 +++ .../fleet/server/services/fleet_server_host.ts | 9 +++++++++ .../preconfiguration/fleet_server_host.ts | 7 +++++-- 11 files changed, 37 insertions(+), 46 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx index 0bec2892d688d..ecbda47baad79 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useMemo, useState } from 'react'; +import React, { useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiText, EuiLink, EuiSteps, EuiSpacer } from '@elastic/eui'; @@ -28,7 +28,7 @@ export const InstallElasticAgentManagedPageStep: React.FC setIsManaged, agentPolicy, enrollmentAPIKey, - settings, + fleetServerHosts, enrolledAgentIds, } = props; @@ -40,10 +40,6 @@ export const InstallElasticAgentManagedPageStep: React.FC const [commandCopied, setCommandCopied] = useState(false); - const fleetServerHosts = useMemo(() => { - return settings?.fleet_server_hosts || []; - }, [settings]); - if (!enrollmentAPIKey) { return ( packageInfoData?.item, [packageInfoData]); - const settings = useMemo(() => settingsData?.item, [settingsData]); const integrationInfo = useMemo(() => { if (!integration) return; @@ -95,6 +92,10 @@ export const CreatePackagePolicyMultiPage: CreatePackagePolicyParams = ({ setOnSplash(false); }; + const fleetServerHostsRequest = useGetFleetServerHosts(); + const fleetServerHosts = + fleetServerHostsRequest.data?.items?.filter((f) => true)?.[0]?.host_urls ?? []; + const cancelUrl = getHref('add_integration_to_policy', { pkgkey, useMultiPageLayout: false, @@ -105,7 +106,9 @@ export const CreatePackagePolicyMultiPage: CreatePackagePolicyParams = ({ if (onSplash || !packageInfo) { return ( { }); const mockedUsedFleetStatus = useFleetStatus as jest.MockedFunction; -const mockedUseGetSettings = useGetSettings as jest.MockedFunction; const mockedUseAuthz = useAuthz as jest.MockedFunction; function renderAgentsApp() { @@ -48,12 +46,6 @@ function renderAgentsApp() { } describe('AgentApp', () => { beforeEach(() => { - mockedUseGetSettings.mockReturnValue({ - isLoading: false, - data: { - item: {}, - }, - } as any); mockedUseAuthz.mockReturnValue({ fleet: { all: true, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.tsx index eb84ffcca477b..27d839dc7af59 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/fleet_server_hosts_flyout/use_fleet_server_host_form.tsx @@ -193,7 +193,11 @@ export function useFleetServerHostsForm( onSuccess, ]); - const isDisabled = isLoading || !hostUrlsInput.hasChanged || hostUrlsInput.props.isInvalid; + const isDisabled = + isLoading || + (!hostUrlsInput.hasChanged && !isDefaultInput.hasChanged && !nameInput.hasChanged) || + hostUrlsInput.props.isInvalid || + nameInput.props.isInvalid; return { isLoading, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/fleet_server_hosts_section.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/fleet_server_hosts_section.tsx index 1bff54df02f08..1114efb814a21 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/fleet_server_hosts_section.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/fleet_server_hosts_section.tsx @@ -10,18 +10,16 @@ import React from 'react'; import { EuiTitle, EuiLink, EuiText, EuiSpacer, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { Settings, FleetServerHost } from '../../../../types'; +import type { FleetServerHost } from '../../../../types'; import { useLink, useStartServices } from '../../../../hooks'; import { FleetServerHostsTable } from '../fleet_server_hosts_table'; export interface FleetServerHostsSectionProps { - settings: Settings; fleetServerHosts: FleetServerHost[]; deleteFleetServerHost: (fleetServerHost: FleetServerHost) => void; } export const FleetServerHostsSection: React.FunctionComponent = ({ - settings, fleetServerHosts, deleteFleetServerHost, }) => { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/index.tsx index 7499fefa1971d..613221600b99b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/index.tsx @@ -8,14 +8,13 @@ import React from 'react'; import { EuiSpacer } from '@elastic/eui'; -import type { Output, Settings, DownloadSource, FleetServerHost } from '../../../../types'; +import type { Output, DownloadSource, FleetServerHost } from '../../../../types'; import { FleetServerHostsSection } from './fleet_server_hosts_section'; import { OutputSection } from './output_section'; import { AgentBinarySection } from './agent_binary_section'; export interface SettingsPageProps { - settings: Settings; outputs: Output[]; fleetServerHosts: FleetServerHost[]; deleteOutput: (output: Output) => void; @@ -25,7 +24,6 @@ export interface SettingsPageProps { } export const SettingsPage: React.FunctionComponent = ({ - settings, outputs, fleetServerHosts, deleteOutput, @@ -37,7 +35,6 @@ export const SettingsPage: React.FunctionComponent = ({ <> diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/index.tsx index 70999a4bc107d..bd0b7124a9c77 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/index.tsx @@ -12,7 +12,6 @@ import { Router, Route, Switch, useHistory, Redirect } from 'react-router-dom'; import { useBreadcrumbs, useGetOutputs, - useGetSettings, useGetDownloadSources, useGetFleetServerHosts, } from '../../hooks'; @@ -33,7 +32,6 @@ export const SettingsApp = withConfirmModalProvider(() => { useBreadcrumbs('settings'); const history = useHistory(); - const settings = useGetSettings(); const outputs = useGetOutputs(); const fleetServerHosts = useGetFleetServerHosts(); const downloadSources = useGetDownloadSources(); @@ -42,28 +40,18 @@ export const SettingsApp = withConfirmModalProvider(() => { const { deleteDownloadSource } = useDeleteDownloadSource(downloadSources.resendRequest); const { deleteFleetServerHost } = useDeleteFleetServerHost(fleetServerHosts.resendRequest); - const resendSettingsRequest = settings.resendRequest; const resendOutputRequest = outputs.resendRequest; const resendDownloadSourceRequest = downloadSources.resendRequest; const resendFleetServerHostsRequest = fleetServerHosts.resendRequest; const onCloseCallback = useCallback(() => { - resendSettingsRequest(); resendOutputRequest(); resendDownloadSourceRequest(); resendFleetServerHostsRequest(); history.replace(pagePathGetters.settings()[1]); - }, [ - resendSettingsRequest, - resendOutputRequest, - resendDownloadSourceRequest, - resendFleetServerHostsRequest, - history, - ]); + }, [resendOutputRequest, resendDownloadSourceRequest, resendFleetServerHostsRequest, history]); if ( - (settings.isLoading && settings.isInitialRequest) || - !settings.data?.item || (outputs.isLoading && outputs.isInitialRequest) || !outputs.data?.items || (fleetServerHosts.isLoading && fleetServerHosts.isInitialRequest) || @@ -152,7 +140,6 @@ export const SettingsApp = withConfirmModalProvider(() => {
{ ], }, }), + useGetSettings: jest.fn().mockReturnValue({ + data: { item: { fleet_server_hosts: ['test'] } }, + }), sendGetOneAgentPolicy: jest.fn().mockResolvedValue({ data: { item: { package_policies: [] } }, }), diff --git a/x-pack/plugins/fleet/server/services/fleet_server_host.ts b/x-pack/plugins/fleet/server/services/fleet_server_host.ts index e63ddf1d905bd..42f03d6e269c0 100644 --- a/x-pack/plugins/fleet/server/services/fleet_server_host.ts +++ b/x-pack/plugins/fleet/server/services/fleet_server_host.ts @@ -7,6 +7,7 @@ import type { SavedObjectsClientContract } from '@kbn/core/server'; +import { normalizeHostsForAgents } from '../../common/services'; import { GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, FLEET_SERVER_HOST_SAVED_OBJECT_TYPE, @@ -40,6 +41,10 @@ export async function createFleetServerHost( } } + if (data.host_urls) { + data.host_urls = data.host_urls.map(normalizeHostsForAgents); + } + const res = await soClient.create( FLEET_SERVER_HOST_SAVED_OBJECT_TYPE, data, @@ -134,6 +139,10 @@ export async function updateFleetServerHost( } } + if (data.host_urls) { + data.host_urls = data.host_urls.map(normalizeHostsForAgents); + } + await soClient.update(FLEET_SERVER_HOST_SAVED_OBJECT_TYPE, id, data); return { diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.ts b/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.ts index 6f3a67cf0c2f2..13de4e8ec64b5 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.ts @@ -8,7 +8,7 @@ import type { SavedObjectsClientContract } from '@kbn/core/server'; import { isEqual } from 'lodash'; -import { decodeCloudId } from '../../../common/services'; +import { decodeCloudId, normalizeHostsForAgents } from '../../../common/services'; import type { FleetConfigType } from '../../config'; import { DEFAULT_FLEET_SERVER_HOST_ID } from '../../constants'; @@ -109,7 +109,10 @@ export async function createOrUpdatePreconfiguredFleetServerHosts( (!existingHost.is_preconfigured || existingHost.is_default !== preconfiguredFleetServerHost.is_default || existingHost.name !== preconfiguredFleetServerHost.name || - !isEqual(existingHost?.host_urls, preconfiguredFleetServerHost.host_urls)); + !isEqual( + existingHost.host_urls.map(normalizeHostsForAgents), + preconfiguredFleetServerHost.host_urls.map(normalizeHostsForAgents) + )); if (isCreate) { await createFleetServerHost(