+ fullWidth
+ isClearable={false}
+ singleSelection={{ asPlainText: true }}
+ placeholder="https://fleet-server-host.com:8220"
+ options={options}
+ customOptionText={i18n.translate(
+ 'xpack.fleet.fleetServerSetup.addFleetServerHostCustomOptionText',
+ {
+ defaultMessage: 'Add {searchValuePlaceholder} as a new Fleet Server host',
+ values: { searchValuePlaceholder: '{searchValue}' },
+ }
+ )}
+ selectedOptions={fleetServerHost ? [{ label: fleetServerHost, value: fleetServerHost }] : []}
+ prepend={
+
+
+
+ }
+ noSuggestions={fleetServerHostSettings.length === 0}
+ data-test-subj="fleetServerHostInput"
+ isDisabled={isDisabled}
+ isInvalid={isInvalid}
+ onChange={handleChange}
+ onCreateOption={handleCreateOption}
+ />
+ );
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/index.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/index.ts
new file mode 100644
index 0000000000000..904271187dc8b
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/components/index.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+export * from './fleet_server_host_combobox';
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/flyout.stories.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/flyout.stories.tsx
new file mode 100644
index 0000000000000..b08c965d084c8
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/flyout.stories.tsx
@@ -0,0 +1,33 @@
+/*
+ * 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 { EuiButton } from '@elastic/eui';
+
+import { FleetServerFlyout as FleetServerFlyoutComponent } from '.';
+
+export const FleetServerFlyout = () => {
+ const [isOpen, setIsOpen] = React.useState(false);
+
+ return (
+
+ setIsOpen(true)}>
+ Show flyout
+
+ {isOpen && setIsOpen(false)} />}
+
+ );
+};
+
+FleetServerFlyout.args = {
+ isCloudEnabled: false,
+};
+
+export default {
+ component: FleetServerFlyout,
+ title: 'Sections/Fleet/Agents/Fleet Server Instructions/In Flyout',
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/index.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/index.ts
new file mode 100644
index 0000000000000..6c702d5433ce0
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/index.ts
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+// These hooks are tightly coupled to each tab of the Fleet server instructions component, and provide
+// all necessary data to drive those UI's
+export * from './use_advanced_form';
+export * from './use_quick_start_form';
+
+// These are individual hooks for one-off consumption. These are typically composed in the hooks above,
+// but exported here to support individual usage.
+export * from './use_wait_for_fleet_server';
+export * from './use_select_fleet_server_policy';
+export * from './use_service_token';
+export * from './use_fleet_server_host';
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form.ts
new file mode 100644
index 0000000000000..5ed9f9faa0372
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { useState } from 'react';
+
+import type { DeploymentMode } from '../steps';
+
+import { useFleetServerHost } from './use_fleet_server_host';
+import { useSelectFleetServerPolicy } from './use_select_fleet_server_policy';
+import { useServiceToken } from './use_service_token';
+import { useWaitForFleetServer } from './use_wait_for_fleet_server';
+
+/**
+ * Provides all data/state required for the "advanced" tab in the Fleet Server instructions/flyout
+ */
+export const useAdvancedForm = (defaultAgentPolicyId?: string) => {
+ const {
+ eligibleFleetServerPolicies,
+ refreshEligibleFleetServerPolicies,
+ fleetServerPolicyId,
+ setFleetServerPolicyId,
+ } = useSelectFleetServerPolicy(defaultAgentPolicyId);
+ const { isFleetServerReady } = useWaitForFleetServer();
+ const { serviceToken, isLoadingServiceToken, generateServiceToken } = useServiceToken();
+ const fleetServerHostForm = useFleetServerHost();
+
+ const [deploymentMode, setDeploymentMode] = useState('quickstart');
+
+ return {
+ eligibleFleetServerPolicies,
+ refreshEligibleFleetServerPolicies,
+ fleetServerPolicyId,
+ setFleetServerPolicyId,
+ isFleetServerReady,
+ serviceToken,
+ isLoadingServiceToken,
+ generateServiceToken,
+ fleetServerHostForm,
+ deploymentMode,
+ setDeploymentMode,
+ };
+};
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
new file mode 100644
index 0000000000000..05eeccf4a9312
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_fleet_server_host.ts
@@ -0,0 +1,103 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+import { useCallback, useEffect, useState } from 'react';
+
+import { sendPutSettings, useGetSettings } from '../../../hooks';
+
+const URL_REGEX = /^(https):\/\/[^\s$.?#].[^\s]*$/gm;
+
+export interface FleetServerHostForm {
+ saveFleetServerHost: () => Promise;
+ fleetServerHost?: string;
+ fleetServerHostSettings: string[];
+ isFleetServerHostSubmitted: boolean;
+ setFleetServerHost: React.Dispatch>;
+ error?: string;
+ validateFleetServerHost: () => boolean;
+}
+
+export const useFleetServerHost = (): FleetServerHostForm => {
+ const [fleetServerHost, setFleetServerHost] = useState();
+ const [isFleetServerHostSubmitted, setIsFleetServerHostSubmitted] = useState(false);
+ const [error, setError] = useState();
+
+ const { data: settings } = useGetSettings();
+
+ useEffect(() => {
+ const settingsFleetServerHosts = settings?.item.fleet_server_hosts ?? [];
+
+ if (settingsFleetServerHosts.length) {
+ setFleetServerHost(settingsFleetServerHosts[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!)) {
+ 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]);
+
+ return {
+ saveFleetServerHost,
+ fleetServerHost,
+ fleetServerHostSettings: settings?.item.fleet_server_hosts ?? [],
+ 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
new file mode 100644
index 0000000000000..84fd39aeec378
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_quick_start_form.ts
@@ -0,0 +1,140 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { useState, useCallback, useEffect } from 'react';
+import { i18n } from '@kbn/i18n';
+
+import { sendCreateAgentPolicy, sendGetOneAgentPolicy, useStartServices } from '../../../hooks';
+
+import type { NewAgentPolicy } from '../../../types';
+
+import { useSelectFleetServerPolicy } from './use_select_fleet_server_policy';
+import { useServiceToken } from './use_service_token';
+import { useFleetServerHost } from './use_fleet_server_host';
+
+const QUICK_START_FLEET_SERVER_POLICY_FIELDS: NewAgentPolicy = {
+ id: 'fleet-server-policy',
+ name: 'Fleet Server Policy',
+ description: 'Fleet Server policy generated by Kibana',
+ namespace: 'default',
+ has_fleet_server: true,
+ monitoring_enabled: ['logs', 'metrics'],
+ is_default_fleet_server: true,
+};
+
+export type QuickStartCreateFormStatus = 'initial' | 'loading' | 'error' | 'success';
+
+export interface QuickStartCreateForm {
+ status: QuickStartCreateFormStatus;
+ error?: string;
+ submit: () => void;
+ fleetServerHost?: string;
+ fleetServerHostSettings: string[];
+ isFleetServerHostSubmitted: boolean;
+ onFleetServerHostChange: (value: string) => void;
+ fleetServerPolicyId?: string;
+ serviceToken?: string;
+}
+
+/**
+ * Provides a unified interface that combines the following operations:
+ * 1. Setting a Fleet Server host in Fleet's settings
+ * 2. Creating an agent policy that contains the `fleet_server` integration
+ * 3. Generating a service token used by Fleet Server
+ */
+export const useQuickStartCreateForm = (): QuickStartCreateForm => {
+ const [status, setStatus] = useState<'initial' | 'loading' | 'error' | 'success'>('initial');
+ const [error, setError] = useState();
+
+ const {
+ fleetServerHost,
+ fleetServerHostSettings,
+ isFleetServerHostSubmitted,
+ setFleetServerHost,
+ validateFleetServerHost,
+ saveFleetServerHost,
+ error: fleetServerError,
+ } = useFleetServerHost();
+
+ // When a validation error is surfaced from the Fleet Server host form, we want to treat it
+ // the same way we do errors from the service token or policy creation steps
+ useEffect(() => {
+ setStatus('error');
+ setError(fleetServerError);
+ }, [fleetServerError]);
+
+ const { notifications } = useStartServices();
+ const { fleetServerPolicyId, setFleetServerPolicyId } = useSelectFleetServerPolicy();
+ const { serviceToken, generateServiceToken } = useServiceToken();
+
+ const onFleetServerHostChange = useCallback(
+ (value: string) => {
+ setFleetServerHost(value);
+ },
+ [setFleetServerHost]
+ );
+
+ const submit = useCallback(async () => {
+ try {
+ if (validateFleetServerHost()) {
+ setStatus('loading');
+ await saveFleetServerHost();
+ await generateServiceToken();
+
+ const existingPolicy = await sendGetOneAgentPolicy(
+ QUICK_START_FLEET_SERVER_POLICY_FIELDS.id!
+ );
+
+ // Don't attempt to create the policy if it's already been created in a previous quick start flow
+ if (existingPolicy.data?.item) {
+ setFleetServerPolicyId(existingPolicy.data?.item.id);
+ } else {
+ const createPolicyResponse = await sendCreateAgentPolicy(
+ QUICK_START_FLEET_SERVER_POLICY_FIELDS,
+ {
+ withSysMonitoring: true,
+ }
+ );
+
+ setFleetServerPolicyId(createPolicyResponse.data?.item.id);
+ }
+
+ setFleetServerHost(fleetServerHost);
+ setStatus('success');
+ }
+ } catch (err) {
+ notifications.toasts.addError(err, {
+ title: i18n.translate('xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle', {
+ defaultMessage: 'Error adding Fleet Server host',
+ }),
+ });
+
+ setStatus('error');
+ setError(err.message);
+ }
+ }, [
+ validateFleetServerHost,
+ saveFleetServerHost,
+ generateServiceToken,
+ setFleetServerHost,
+ fleetServerHost,
+ setFleetServerPolicyId,
+ notifications.toasts,
+ ]);
+
+ return {
+ status,
+ error,
+ submit,
+ fleetServerPolicyId,
+ fleetServerHost,
+ fleetServerHostSettings,
+ isFleetServerHostSubmitted,
+ onFleetServerHostChange,
+ serviceToken,
+ };
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_select_fleet_server_policy.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_select_fleet_server_policy.ts
new file mode 100644
index 0000000000000..add318e85a7ae
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_select_fleet_server_policy.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { useEffect, useMemo, useState } from 'react';
+
+import { useGetAgentPolicies } from '../../../hooks';
+import { policyHasFleetServer } from '../../../services';
+
+export const useSelectFleetServerPolicy = (defaultAgentPolicyId?: string) => {
+ const [fleetServerPolicyId, setFleetServerPolicyId] = useState(
+ defaultAgentPolicyId
+ );
+ const { data: agentPoliciesData, resendRequest } = useGetAgentPolicies({
+ full: true,
+ });
+
+ const eligibleFleetServerPolicies = useMemo(
+ () =>
+ agentPoliciesData
+ ? agentPoliciesData.items?.filter((item) => policyHasFleetServer(item))
+ : [],
+ [agentPoliciesData]
+ );
+
+ useEffect(() => {
+ // Default to the first policy found with a fleet server integration installed
+ if (eligibleFleetServerPolicies.length && !fleetServerPolicyId) {
+ setFleetServerPolicyId(eligibleFleetServerPolicies[0].id);
+ }
+ }, [eligibleFleetServerPolicies, fleetServerPolicyId]);
+
+ return {
+ fleetServerPolicyId,
+ setFleetServerPolicyId,
+ eligibleFleetServerPolicies,
+ refreshEligibleFleetServerPolicies: resendRequest,
+ };
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_service_token.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_service_token.ts
new file mode 100644
index 0000000000000..3b729a6776b52
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_service_token.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { useState, useCallback } from 'react';
+import { i18n } from '@kbn/i18n';
+
+import { useStartServices, sendGenerateServiceToken } from '../../../hooks';
+
+export const useServiceToken = () => {
+ const { notifications } = useStartServices();
+ const [serviceToken, setServiceToken] = useState();
+ const [isLoadingServiceToken, setIsLoadingServiceToken] = useState(false);
+
+ const generateServiceToken = useCallback(async () => {
+ setIsLoadingServiceToken(true);
+ try {
+ const { data } = await sendGenerateServiceToken();
+ if (data?.value) {
+ setServiceToken(data?.value);
+ }
+ } catch (err) {
+ notifications.toasts.addError(err, {
+ title: i18n.translate('xpack.fleet.fleetServerSetup.errorGeneratingTokenTitleText', {
+ defaultMessage: 'Error generating token',
+ }),
+ });
+ } finally {
+ setIsLoadingServiceToken(false);
+ }
+ }, [notifications.toasts]);
+
+ return { serviceToken, isLoadingServiceToken, generateServiceToken };
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_wait_for_fleet_server.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_wait_for_fleet_server.ts
new file mode 100644
index 0000000000000..4da59560e408e
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/hooks/use_wait_for_fleet_server.ts
@@ -0,0 +1,61 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+import { useEffect, useState } from 'react';
+
+import { sendGetFleetStatus, useStartServices } from '../../../hooks';
+
+const REFRESH_INTERVAL = 10000;
+
+/**
+ * Polls the Fleet status endpoint until the `fleet_server` requirement does not appear
+ * in the `missing_requirements` list.
+ */
+export const useWaitForFleetServer = () => {
+ const [isFleetServerReady, setIsFleetServerReady] = useState(false);
+ const { notifications } = useStartServices();
+
+ useEffect(() => {
+ let interval: ReturnType | null = null;
+
+ if (!isFleetServerReady) {
+ interval = setInterval(async () => {
+ try {
+ const res = await sendGetFleetStatus();
+
+ if (res.error) {
+ throw res.error;
+ }
+ if (res.data?.isReady && !res.data?.missing_requirements?.includes('fleet_server')) {
+ setIsFleetServerReady(true);
+
+ if (interval) {
+ clearInterval(interval);
+ }
+ }
+ } catch (err) {
+ notifications.toasts.addError(err, {
+ title: i18n.translate('xpack.fleet.fleetServerSetup.errorRefreshingFleetServerStatus', {
+ defaultMessage: 'Error refreshing Fleet Server status',
+ }),
+ });
+ }
+ }, REFRESH_INTERVAL);
+ }
+
+ const cleanup = () => {
+ if (interval) {
+ clearInterval(interval);
+ }
+ };
+
+ return cleanup;
+ }, [notifications.toasts, isFleetServerReady]);
+
+ return { isFleetServerReady };
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/index.tsx
new file mode 100644
index 0000000000000..6c48b499b9553
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/index.tsx
@@ -0,0 +1,154 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { useState } from 'react';
+import {
+ EuiFlexGroup,
+ EuiFlyout,
+ EuiFlyoutBody,
+ EuiFlyoutHeader,
+ EuiLink,
+ EuiSpacer,
+ EuiTab,
+ EuiTabs,
+ EuiText,
+ EuiTitle,
+} from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n-react';
+
+import styled from 'styled-components';
+
+import { useStartServices } from '../../hooks';
+
+import { QuickStartTab } from './quick_start_tab';
+import { AdvancedTab } from './advanced_tab';
+
+const ContentWrapper = styled(EuiFlexGroup)`
+ height: 100%;
+ margin: 0 auto;
+`;
+
+interface Props {
+ onClose: () => void;
+}
+
+const useFleetServerTabs = () => {
+ const [currentTab, setCurrentTab] = useState('quickStart');
+
+ const quickStartTab = {
+ id: 'quickStart',
+ name: 'Quick Start',
+ content: ,
+ };
+
+ const advancedTab = {
+ id: 'advanced',
+ name: 'Advanced',
+ content: ,
+ };
+
+ const currentTabContent =
+ currentTab === 'quickStart' ? quickStartTab.content : advancedTab.content;
+
+ return { tabs: [quickStartTab, advancedTab], currentTab, setCurrentTab, currentTabContent };
+};
+
+const Header: React.FunctionComponent<{
+ isFlyout?: boolean;
+ currentTab: string;
+ tabs: Array<{ id: string; name: string; content: React.ReactNode }>;
+ onTabClick: (id: string) => void;
+}> = ({ isFlyout = false, currentTab: currentTabId, tabs, onTabClick }) => {
+ const { docLinks } = useStartServices();
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+ ),
+ }}
+ />
+
+
+
+
+
+ {tabs.map((tab) => (
+ onTabClick(tab.id)}
+ >
+ {tab.name}
+
+ ))}
+
+ >
+ );
+};
+
+// Renders instructions inside of a flyout
+export const FleetServerFlyout: React.FunctionComponent = ({ onClose }) => {
+ const { tabs, currentTab, setCurrentTab, currentTabContent } = useFleetServerTabs();
+
+ return (
+
+
+ setCurrentTab(id)}
+ isFlyout
+ />
+
+
+ {currentTabContent}
+
+ );
+};
+
+// Renders instructions directly
+export const FleetServerInstructions: React.FunctionComponent = () => {
+ const { tabs, currentTab, setCurrentTab, currentTabContent } = useFleetServerTabs();
+
+ return (
+
+ setCurrentTab(id)} />
+
+
+
+ {currentTabContent}
+
+ );
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/instructions.stories.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/instructions.stories.tsx
new file mode 100644
index 0000000000000..9993fab723a44
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/instructions.stories.tsx
@@ -0,0 +1,23 @@
+/*
+ * 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 { FleetServerInstructions as FleetServerInstructionsComponent } from '.';
+
+export const FleetServerInstructions = () => {
+ return ;
+};
+
+FleetServerInstructions.args = {
+ isCloudEnabled: false,
+};
+
+export default {
+ component: FleetServerInstructions,
+ title: 'Sections/Fleet/Agents/Fleet Server Instructions/Without Flyout',
+};
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
new file mode 100644
index 0000000000000..cf8abc2fe9e16
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx
@@ -0,0 +1,41 @@
+/*
+ * 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 { EuiSteps } from '@elastic/eui';
+
+import { useQuickStartCreateForm, useWaitForFleetServer } from './hooks';
+
+import {
+ getGettingStartedStep,
+ getConfirmFleetServerConnectionStep,
+ getInstallFleetServerStep,
+} from './steps';
+
+export const QuickStartTab: React.FunctionComponent = () => {
+ const quickStartCreateForm = useQuickStartCreateForm();
+ const { isFleetServerReady } = useWaitForFleetServer();
+
+ const steps = [
+ getGettingStartedStep({
+ quickStartCreateForm,
+ }),
+ getInstallFleetServerStep({
+ isFleetServerReady,
+ fleetServerHost: quickStartCreateForm.fleetServerHost,
+ fleetServerPolicyId: quickStartCreateForm.fleetServerPolicyId,
+ serviceToken: quickStartCreateForm.serviceToken,
+ disabled: quickStartCreateForm.status !== 'success',
+ }),
+ getConfirmFleetServerConnectionStep({
+ isFleetServerReady,
+ disabled: quickStartCreateForm.status !== 'success',
+ }),
+ ];
+
+ return ;
+};
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
new file mode 100644
index 0000000000000..e64e23f039f89
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/add_fleet_server_host.tsx
@@ -0,0 +1,165 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { useState, useCallback } from 'react';
+import type { EuiStepProps } from '@elastic/eui';
+import {
+ EuiButton,
+ EuiCallOut,
+ EuiCode,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiForm,
+ EuiFormErrorText,
+ EuiLink,
+ EuiSpacer,
+ EuiText,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n-react';
+
+import { useStartServices, useLink } from '../../../hooks';
+import type { FleetServerHostForm } from '../hooks';
+import { FleetServerHostComboBox } from '../components';
+
+export const getAddFleetServerHostStep = ({
+ fleetServerHostForm,
+ disabled,
+}: {
+ fleetServerHostForm: FleetServerHostForm;
+ disabled: boolean;
+}): EuiStepProps => {
+ return {
+ title: i18n.translate('xpack.fleet.fleetServerSetup.addFleetServerHostStepTitle', {
+ defaultMessage: 'Add your Fleet Server host',
+ }),
+ status: disabled ? 'disabled' : undefined,
+ children: disabled ? null : (
+
+ ),
+ };
+};
+
+export const AddFleetServerHostStepContent = ({
+ fleetServerHostForm,
+}: {
+ fleetServerHostForm: FleetServerHostForm;
+}) => {
+ const {
+ fleetServerHost,
+ fleetServerHostSettings,
+ setFleetServerHost,
+ validateFleetServerHost,
+ saveFleetServerHost,
+ error,
+ } = fleetServerHostForm;
+
+ const [isLoading, setIsLoading] = useState(false);
+ const [submittedFleetServerHost, setSubmittedFleetServerHost] = useState();
+ const { notifications } = useStartServices();
+ const { getHref } = useLink();
+
+ const onSubmit = useCallback(async () => {
+ try {
+ setSubmittedFleetServerHost('');
+ setIsLoading(true);
+
+ if (validateFleetServerHost()) {
+ await saveFleetServerHost();
+ setSubmittedFleetServerHost(fleetServerHost);
+ }
+ } catch (err) {
+ notifications.toasts.addError(err, {
+ title: i18n.translate('xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle', {
+ defaultMessage: 'Error adding Fleet Server host',
+ }),
+ });
+ } finally {
+ setIsLoading(false);
+ }
+ }, [validateFleetServerHost, saveFleetServerHost, fleetServerHost, notifications.toasts]);
+
+ const onChange = useCallback(
+ (host: string) => {
+ setFleetServerHost(host);
+
+ if (error) {
+ validateFleetServerHost();
+ }
+ },
+ [error, setFleetServerHost, validateFleetServerHost]
+ );
+
+ return (
+
+
+ 8220 }}
+ />
+
+
+
+
+
+ {error && {error}}
+
+
+
+
+
+
+
+ {submittedFleetServerHost && (
+ <>
+
+
+ }
+ >
+
+
+
+ ),
+ }}
+ />
+
+ >
+ )}
+
+ );
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/confirm_fleet_server_connection.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/confirm_fleet_server_connection.tsx
new file mode 100644
index 0000000000000..5aa5c01a108a4
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/confirm_fleet_server_connection.tsx
@@ -0,0 +1,66 @@
+/*
+ * 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, { useContext } from 'react';
+
+import type { EuiStepProps } from '@elastic/eui';
+import { EuiButton, EuiLoadingSpinner, EuiSpacer } from '@elastic/eui';
+import { EuiText } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n-react';
+
+import { agentFlyoutContext } from '../../../sections/agents';
+
+export function getConfirmFleetServerConnectionStep({
+ disabled,
+ isFleetServerReady,
+}: {
+ disabled: boolean;
+ isFleetServerReady: boolean;
+}): EuiStepProps {
+ return {
+ title: isFleetServerReady
+ ? i18n.translate('xpack.fleet.fleetServerFlyout.confirmConnectionSuccessTitle', {
+ defaultMessage: 'Fleet Server connected',
+ })
+ : i18n.translate('xpack.fleet.fleetServerFlyout.confirmConnectionTitle', {
+ defaultMessage: 'Confirm connection',
+ }),
+ status: isFleetServerReady ? 'complete' : 'disabled',
+ children: !disabled && (
+
+ ),
+ };
+}
+
+const ConfirmFleetServerConnectionStepContent: React.FunctionComponent<{
+ isFleetServerReady: boolean;
+}> = ({ isFleetServerReady }) => {
+ const addAgentFlyout = useContext(agentFlyoutContext);
+
+ return isFleetServerReady ? (
+ <>
+
+
+
+
+
+
+
+
+
+ >
+ ) : (
+
+ );
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/create_service_token.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/create_service_token.tsx
new file mode 100644
index 0000000000000..dcfa150c2d32c
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/create_service_token.tsx
@@ -0,0 +1,136 @@
+/*
+ * 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 styled from 'styled-components';
+
+import type { EuiStepProps } from '@elastic/eui';
+import {
+ EuiButton,
+ EuiCallOut,
+ EuiCodeBlock,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiSpacer,
+ EuiText,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n-react';
+
+const FlexItemWithMinWidth = styled(EuiFlexItem)`
+ min-width: 0px;
+ max-width: 100%;
+`;
+
+export const ContentWrapper = styled(EuiFlexGroup)`
+ height: 100%;
+ margin: 0 auto;
+ max-width: 800px;
+`;
+
+// Otherwise the copy button is over the text
+const CommandCode = styled.div.attrs(() => {
+ return {
+ className: 'eui-textBreakAll',
+ };
+})`
+ margin-right: ${(props) => props.theme.eui.paddingSizes.m};
+`;
+
+export const getGenerateServiceTokenStep = ({
+ disabled = false,
+ serviceToken,
+ generateServiceToken,
+ isLoadingServiceToken,
+}: {
+ disabled?: boolean;
+ serviceToken?: string;
+ generateServiceToken: () => void;
+ isLoadingServiceToken: boolean;
+}): EuiStepProps => {
+ return {
+ title: i18n.translate('xpack.fleet.fleetServerSetup.stepGenerateServiceTokenTitle', {
+ defaultMessage: 'Generate a service token',
+ }),
+ status: disabled ? 'disabled' : undefined,
+ children: !disabled && (
+
+ ),
+ };
+};
+
+const ServiceTokenStepContent: React.FunctionComponent<{
+ serviceToken?: string;
+ generateServiceToken: () => void;
+ isLoadingServiceToken: boolean;
+}> = ({ serviceToken, generateServiceToken, isLoadingServiceToken }) => {
+ return (
+ <>
+
+
+
+
+ {!serviceToken ? (
+
+
+ {
+ generateServiceToken();
+ }}
+ data-test-subj="fleetServerGenerateServiceTokenBtn"
+ >
+
+
+
+
+ ) : (
+ <>
+
+ }
+ />
+
+
+
+
+
+
+
+
+
+ {serviceToken}
+
+
+
+ >
+ )}
+ >
+ );
+};
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
new file mode 100644
index 0000000000000..50e9b6b72002c
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/get_started.tsx
@@ -0,0 +1,130 @@
+/*
+ * 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 type { EuiStepProps } from '@elastic/eui';
+import {
+ EuiButton,
+ EuiCallOut,
+ EuiCode,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiForm,
+ EuiFormErrorText,
+ EuiLink,
+ EuiSpacer,
+ EuiText,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n-react';
+
+import { useLink } from '../../../hooks';
+
+import type { QuickStartCreateForm } from '../hooks';
+import { FleetServerHostComboBox } from '../components';
+
+export function getGettingStartedStep({
+ quickStartCreateForm,
+}: {
+ quickStartCreateForm: QuickStartCreateForm;
+}): EuiStepProps {
+ return {
+ title: i18n.translate('xpack.fleet.fleetServerFlyout.getStartedTitle', {
+ defaultMessage: 'Get started with Fleet Server',
+ }),
+ status: quickStartCreateForm.status === 'success' ? 'complete' : 'current',
+ children: ,
+ };
+}
+
+const GettingStartedStepContent: React.FunctionComponent<{
+ quickStartCreateForm: QuickStartCreateForm;
+}> = ({ quickStartCreateForm }) => {
+ const { getHref } = useLink();
+
+ const { fleetServerHost, fleetServerHostSettings, onFleetServerHostChange } =
+ quickStartCreateForm;
+
+ if (quickStartCreateForm.status === 'success') {
+ return (
+
+
+ {fleetServerHost},
+ fleetSettingsLink: (
+
+
+
+ ),
+ }}
+ />
+
+
+ );
+ }
+
+ return (
+ <>
+
+ 8220 }}
+ />
+
+
+
+
+
+
+
+
+
+ {quickStartCreateForm.status === 'error' && (
+ {quickStartCreateForm.error}
+ )}
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/index.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/index.ts
new file mode 100644
index 0000000000000..11adf2693927b
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/index.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export * from './add_fleet_server_host';
+export * from './confirm_fleet_server_connection';
+export * from './create_service_token';
+export * from './get_started';
+export * from './install_fleet_server';
+export * from './select_agent_policy';
+export * from './set_deployment_mode';
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/install_fleet_server.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/install_fleet_server.tsx
new file mode 100644
index 0000000000000..dac9555ba3149
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/install_fleet_server.tsx
@@ -0,0 +1,97 @@
+/*
+ * 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 type { EuiStepProps } from '@elastic/eui';
+import { EuiSpacer, EuiText } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n-react';
+
+import type { PLATFORM_TYPE } from '../../../hooks';
+import { useDefaultOutput, useKibanaVersion } from '../../../hooks';
+
+import { PlatformSelector } from '../..';
+
+import { getInstallCommandForPlatform } from '../utils';
+
+export function getInstallFleetServerStep({
+ isFleetServerReady,
+ disabled,
+ serviceToken,
+ fleetServerHost,
+ fleetServerPolicyId,
+}: {
+ isFleetServerReady: boolean;
+ disabled: boolean;
+ serviceToken?: string;
+ fleetServerHost?: string;
+ fleetServerPolicyId?: string;
+}): EuiStepProps {
+ return {
+ title: i18n.translate('xpack.fleet.fleetServerFlyout.installFleetServerTitle', {
+ defaultMessage: 'Install Fleet Server to a centralized host',
+ }),
+ status: disabled ? 'disabled' : isFleetServerReady ? 'complete' : 'incomplete',
+ children: !disabled && (
+
+ ),
+ };
+}
+
+const InstallFleetServerStepContent: React.FunctionComponent<{
+ serviceToken?: string;
+ fleetServerHost?: string;
+ fleetServerPolicyId?: string;
+}> = ({ serviceToken, fleetServerHost, fleetServerPolicyId }) => {
+ const kibanaVersion = useKibanaVersion();
+ const { output } = useDefaultOutput();
+
+ const installCommands = (['linux', 'mac', 'windows', 'deb', 'rpm'] as PLATFORM_TYPE[]).reduce(
+ (acc, platform) => {
+ acc[platform] = getInstallCommandForPlatform(
+ platform,
+ output?.hosts?.[0] ?? '',
+ serviceToken ?? '',
+ fleetServerPolicyId,
+ fleetServerHost,
+ false,
+ output?.ca_trusted_fingerprint,
+ kibanaVersion
+ );
+
+ return acc;
+ },
+ {} as Record
+ );
+
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/select_agent_policy.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/select_agent_policy.tsx
new file mode 100644
index 0000000000000..778716bacb02e
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/select_agent_policy.tsx
@@ -0,0 +1,81 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { EuiStepProps } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import React, { useEffect } from 'react';
+
+import { SelectCreateAgentPolicy } from '../..';
+
+import type { GetAgentPoliciesResponseItem } from '../../../types';
+
+export const getSelectAgentPolicyStep = ({
+ policyId,
+ setPolicyId,
+ eligibleFleetServerPolicies,
+ refreshEligibleFleetServerPolicies,
+}: {
+ policyId?: string;
+ setPolicyId: (v?: string) => void;
+ eligibleFleetServerPolicies: GetAgentPoliciesResponseItem[];
+ refreshEligibleFleetServerPolicies: () => void;
+}): EuiStepProps => {
+ return {
+ title:
+ eligibleFleetServerPolicies.length === 0
+ ? i18n.translate('xpack.fleet.fleetServerSetup.stepCreateAgentPolicyTitle', {
+ defaultMessage: 'Create a policy for Fleet Server',
+ })
+ : i18n.translate('xpack.fleet.fleetServerSetup.stepSelectAgentPolicyTitle', {
+ defaultMessage: 'Select a policy for Fleet Server',
+ }),
+ status: policyId ? 'complete' : undefined,
+ children: (
+
+ ),
+ };
+};
+
+const SelectAgentPolicyStepContent: React.FunctionComponent<{
+ policyId?: string;
+ setPolicyId: (v?: string) => void;
+ eligibleFleetServerPolicies: GetAgentPoliciesResponseItem[];
+ refreshEligibleFleetServerPolicies: () => void;
+}> = ({
+ policyId,
+ setPolicyId,
+ eligibleFleetServerPolicies,
+ refreshEligibleFleetServerPolicies,
+}) => {
+ useEffect(() => {
+ // Select default value
+ if (eligibleFleetServerPolicies.length && !policyId) {
+ setPolicyId(eligibleFleetServerPolicies[0].id);
+ }
+ }, [eligibleFleetServerPolicies, policyId, setPolicyId]);
+
+ const setSelectedPolicyId = (agentPolicyId?: string) => {
+ setPolicyId(agentPolicyId);
+ };
+
+ return (
+
+ );
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/set_deployment_mode.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/set_deployment_mode.tsx
new file mode 100644
index 0000000000000..9833fc1d4527f
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/set_deployment_mode.tsx
@@ -0,0 +1,117 @@
+/*
+ * 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, useMemo } from 'react';
+
+import type { EuiStepProps } from '@elastic/eui';
+import { EuiRadioGroup, EuiSpacer } from '@elastic/eui';
+import { EuiText } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n-react';
+
+export type DeploymentMode = 'production' | 'quickstart';
+
+export const getSetDeploymentModeStep = ({
+ deploymentMode,
+ setDeploymentMode,
+ disabled,
+}: {
+ deploymentMode: DeploymentMode;
+ setDeploymentMode: (v: DeploymentMode) => void;
+ disabled: boolean;
+}): EuiStepProps => {
+ return {
+ title: i18n.translate('xpack.fleet.fleetServerSetup.stepDeploymentModeTitle', {
+ defaultMessage: 'Choose a deployment mode for security',
+ }),
+ status: disabled ? 'disabled' : undefined,
+ children: disabled ? null : (
+
+ ),
+ };
+};
+
+const DeploymentModeStepContent = ({
+ deploymentMode,
+ setDeploymentMode,
+}: {
+ deploymentMode: DeploymentMode;
+ setDeploymentMode: (v: DeploymentMode) => void;
+}) => {
+ const onChangeCallback = useCallback(
+ (v: string) => {
+ const value = v.split('_')[0];
+ if (value === 'production' || value === 'quickstart') {
+ setDeploymentMode(value);
+ }
+ },
+ [setDeploymentMode]
+ );
+
+ // radio id has to be unique so that the component works even if appears twice in DOM (Agents tab, Add agent flyout)
+ const radioSuffix = useMemo(() => Date.now(), []);
+
+ return (
+ <>
+
+
+
+
+
+
+
+ ),
+ }}
+ />
+ ),
+ },
+ {
+ id: `production_${radioSuffix}`,
+ label: (
+
+
+
+ ),
+ }}
+ />
+ ),
+ },
+ ]}
+ idSelected={`${deploymentMode}_${radioSuffix}`}
+ onChange={onChangeCallback}
+ name={`radio group ${radioSuffix}`}
+ />
+ >
+ );
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/index.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/index.ts
new file mode 100644
index 0000000000000..fcbab4728cbe2
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export * from './install_command_utils';
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils.test.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.test.ts
similarity index 56%
rename from x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils.test.ts
rename to x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.test.ts
index 774b7871f0353..89a246c5c6265 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils.test.ts
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.test.ts
@@ -10,50 +10,85 @@ import { getInstallCommandForPlatform } from './install_command_utils';
describe('getInstallCommandForPlatform', () => {
describe('without policy id', () => {
it('should return the correct command if the the policyId is not set for linux', () => {
- const res = getInstallCommandForPlatform('http://elasticsearch:9200', 'service-token-1');
+ const res = getInstallCommandForPlatform(
+ 'linux',
+ 'http://elasticsearch:9200',
+ 'service-token-1'
+ );
- expect(res.linux).toMatchInlineSnapshot(`
- "sudo ./elastic-agent install \\\\
+ expect(res).toMatchInlineSnapshot(`
+ "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip \\\\
+ tar xzvf elastic-agent--linux-x86_64.zip \\\\
+ cd elastic-agent--linux-x86_64 \\\\
+ sudo ./elastic-agent install \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1"
`);
});
it('should return the correct command if the the policyId is not set for mac', () => {
- const res = getInstallCommandForPlatform('http://elasticsearch:9200', 'service-token-1');
+ const res = getInstallCommandForPlatform(
+ 'mac',
+ 'http://elasticsearch:9200',
+ 'service-token-1'
+ );
- expect(res.mac).toMatchInlineSnapshot(`
- "sudo ./elastic-agent install \\\\
+ expect(res).toMatchInlineSnapshot(`
+ "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--darwin-x86_64.tar.gz \\\\
+ tar xzvf elastic-agent--darwin-x86_64.tar.gz \\\\
+ cd elastic-agent--darwin-x86_64 \\\\
+ sudo ./elastic-agent install \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1"
`);
});
it('should return the correct command if the the policyId is not set for windows', () => {
- const res = getInstallCommandForPlatform('http://elasticsearch:9200', 'service-token-1');
+ const res = getInstallCommandForPlatform(
+ 'windows',
+ 'http://elasticsearch:9200',
+ 'service-token-1'
+ );
- expect(res.windows).toMatchInlineSnapshot(`
- ".\\\\elastic-agent.exe install \`
+ expect(res).toMatchInlineSnapshot(`
+ "wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--windows-x86_64.tar.gz -OutFile elastic-agent--windows-x86_64.tar.gz \`
+ Expand-Archive .\\\\elastic-agent--windows-x86_64.tar.gz \`
+ cd elastic-agent--windows-x86_64\`
+ .\\\\elastic-agent.exe install \`
--fleet-server-es=http://elasticsearch:9200 \`
--fleet-server-service-token=service-token-1"
`);
});
it('should return the correct command if the the policyId is not set for rpm', () => {
- const res = getInstallCommandForPlatform('http://elasticsearch:9200', 'service-token-1');
+ const res = getInstallCommandForPlatform(
+ 'rpm',
+ 'http://elasticsearch:9200',
+ 'service-token-1'
+ );
- expect(res.rpm).toMatchInlineSnapshot(`
- "sudo elastic-agent enroll \\\\
+ expect(res).toMatchInlineSnapshot(`
+ "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--x86_64.rpm \\\\
+ tar xzvf elastic-agent--x86_64.rpm \\\\
+ cd elastic-agent--x86_64 \\\\
+ sudo elastic-agent enroll \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1"
`);
});
it('should return the correct command if the the policyId is not set for deb', () => {
- const res = getInstallCommandForPlatform('http://elasticsearch:9200', 'service-token-1');
+ const res = getInstallCommandForPlatform(
+ 'deb',
+ 'http://elasticsearch:9200',
+ 'service-token-1'
+ );
- expect(res.deb).toMatchInlineSnapshot(`
- "sudo elastic-agent enroll \\\\
+ expect(res).toMatchInlineSnapshot(`
+ "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--amd64.deb \\\\
+ tar xzvf elastic-agent--amd64.deb \\\\
+ cd elastic-agent--amd64 \\\\
+ sudo elastic-agent enroll \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1"
`);
@@ -61,6 +96,7 @@ describe('getInstallCommandForPlatform', () => {
it('should return the correct command sslCATrustedFingerprint option is passed', () => {
const res = getInstallCommandForPlatform(
+ 'linux',
'http://elasticsearch:9200',
'service-token-1',
undefined,
@@ -69,8 +105,11 @@ describe('getInstallCommandForPlatform', () => {
'fingerprint123456'
);
- expect(res.linux).toMatchInlineSnapshot(`
- "sudo ./elastic-agent install \\\\
+ expect(res).toMatchInlineSnapshot(`
+ "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip \\\\
+ tar xzvf elastic-agent--linux-x86_64.zip \\\\
+ cd elastic-agent--linux-x86_64 \\\\
+ sudo ./elastic-agent install \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1 \\\\
--fleet-server-es-ca-trusted-fingerprint=fingerprint123456"
@@ -81,13 +120,17 @@ describe('getInstallCommandForPlatform', () => {
describe('with policy id', () => {
it('should return the correct command if the the policyId is set for linux', () => {
const res = getInstallCommandForPlatform(
+ 'linux',
'http://elasticsearch:9200',
'service-token-1',
'policy-1'
);
- expect(res.linux).toMatchInlineSnapshot(`
- "sudo ./elastic-agent install \\\\
+ expect(res).toMatchInlineSnapshot(`
+ "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip \\\\
+ tar xzvf elastic-agent--linux-x86_64.zip \\\\
+ cd elastic-agent--linux-x86_64 \\\\
+ sudo ./elastic-agent install \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1 \\\\
--fleet-server-policy=policy-1"
@@ -96,13 +139,17 @@ describe('getInstallCommandForPlatform', () => {
it('should return the correct command if the the policyId is set for mac', () => {
const res = getInstallCommandForPlatform(
+ 'mac',
'http://elasticsearch:9200',
'service-token-1',
'policy-1'
);
- expect(res.mac).toMatchInlineSnapshot(`
- "sudo ./elastic-agent install \\\\
+ expect(res).toMatchInlineSnapshot(`
+ "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--darwin-x86_64.tar.gz \\\\
+ tar xzvf elastic-agent--darwin-x86_64.tar.gz \\\\
+ cd elastic-agent--darwin-x86_64 \\\\
+ sudo ./elastic-agent install \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1 \\\\
--fleet-server-policy=policy-1"
@@ -111,13 +158,17 @@ describe('getInstallCommandForPlatform', () => {
it('should return the correct command if the the policyId is set for windows', () => {
const res = getInstallCommandForPlatform(
+ 'windows',
'http://elasticsearch:9200',
'service-token-1',
'policy-1'
);
- expect(res.windows).toMatchInlineSnapshot(`
- ".\\\\elastic-agent.exe install \`
+ expect(res).toMatchInlineSnapshot(`
+ "wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--windows-x86_64.tar.gz -OutFile elastic-agent--windows-x86_64.tar.gz \`
+ Expand-Archive .\\\\elastic-agent--windows-x86_64.tar.gz \`
+ cd elastic-agent--windows-x86_64\`
+ .\\\\elastic-agent.exe install \`
--fleet-server-es=http://elasticsearch:9200 \`
--fleet-server-service-token=service-token-1 \`
--fleet-server-policy=policy-1"
@@ -126,13 +177,17 @@ describe('getInstallCommandForPlatform', () => {
it('should return the correct command if the the policyId is set for rpm', () => {
const res = getInstallCommandForPlatform(
+ 'rpm',
'http://elasticsearch:9200',
'service-token-1',
'policy-1'
);
- expect(res.rpm).toMatchInlineSnapshot(`
- "sudo elastic-agent enroll \\\\
+ expect(res).toMatchInlineSnapshot(`
+ "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--x86_64.rpm \\\\
+ tar xzvf elastic-agent--x86_64.rpm \\\\
+ cd elastic-agent--x86_64 \\\\
+ sudo elastic-agent enroll \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1 \\\\
--fleet-server-policy=policy-1"
@@ -141,13 +196,17 @@ describe('getInstallCommandForPlatform', () => {
it('should return the correct command if the the policyId is set for deb', () => {
const res = getInstallCommandForPlatform(
+ 'deb',
'http://elasticsearch:9200',
'service-token-1',
'policy-1'
);
- expect(res.deb).toMatchInlineSnapshot(`
- "sudo elastic-agent enroll \\\\
+ expect(res).toMatchInlineSnapshot(`
+ "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--amd64.deb \\\\
+ tar xzvf elastic-agent--amd64.deb \\\\
+ cd elastic-agent--amd64 \\\\
+ sudo elastic-agent enroll \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1 \\\\
--fleet-server-policy=policy-1"
@@ -158,6 +217,7 @@ describe('getInstallCommandForPlatform', () => {
describe('with policy id and fleet server host and production deployment', () => {
it('should return the correct command if the the policyId is set for linux', () => {
const res = getInstallCommandForPlatform(
+ 'linux',
'http://elasticsearch:9200',
'service-token-1',
'policy-1',
@@ -165,8 +225,11 @@ describe('getInstallCommandForPlatform', () => {
true
);
- expect(res.linux).toMatchInlineSnapshot(`
- "sudo ./elastic-agent install --url=http://fleetserver:8220 \\\\
+ expect(res).toMatchInlineSnapshot(`
+ "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--linux-x86_64.zip \\\\
+ tar xzvf elastic-agent--linux-x86_64.zip \\\\
+ cd elastic-agent--linux-x86_64 \\\\
+ sudo ./elastic-agent install--url=http://fleetserver:8220 \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1 \\\\
--fleet-server-policy=policy-1 \\\\
@@ -179,6 +242,7 @@ describe('getInstallCommandForPlatform', () => {
it('should return the correct command if the the policyId is set for mac', () => {
const res = getInstallCommandForPlatform(
+ 'mac',
'http://elasticsearch:9200',
'service-token-1',
'policy-1',
@@ -186,8 +250,11 @@ describe('getInstallCommandForPlatform', () => {
true
);
- expect(res.mac).toMatchInlineSnapshot(`
- "sudo ./elastic-agent install --url=http://fleetserver:8220 \\\\
+ expect(res).toMatchInlineSnapshot(`
+ "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--darwin-x86_64.tar.gz \\\\
+ tar xzvf elastic-agent--darwin-x86_64.tar.gz \\\\
+ cd elastic-agent--darwin-x86_64 \\\\
+ sudo ./elastic-agent install --url=http://fleetserver:8220 \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1 \\\\
--fleet-server-policy=policy-1 \\\\
@@ -200,6 +267,7 @@ describe('getInstallCommandForPlatform', () => {
it('should return the correct command if the the policyId is set for windows', () => {
const res = getInstallCommandForPlatform(
+ 'windows',
'http://elasticsearch:9200',
'service-token-1',
'policy-1',
@@ -207,8 +275,11 @@ describe('getInstallCommandForPlatform', () => {
true
);
- expect(res.windows).toMatchInlineSnapshot(`
- ".\\\\elastic-agent.exe install --url=http://fleetserver:8220 \`
+ expect(res).toMatchInlineSnapshot(`
+ "wget https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--windows-x86_64.tar.gz -OutFile elastic-agent--windows-x86_64.tar.gz \`
+ Expand-Archive .\\\\elastic-agent--windows-x86_64.tar.gz \`
+ cd elastic-agent--windows-x86_64\`
+ .\\\\elastic-agent.exe install --url=http://fleetserver:8220 \`
--fleet-server-es=http://elasticsearch:9200 \`
--fleet-server-service-token=service-token-1 \`
--fleet-server-policy=policy-1 \`
@@ -221,6 +292,7 @@ describe('getInstallCommandForPlatform', () => {
it('should return the correct command if the the policyId is set for rpm', () => {
const res = getInstallCommandForPlatform(
+ 'rpm',
'http://elasticsearch:9200',
'service-token-1',
'policy-1',
@@ -228,8 +300,11 @@ describe('getInstallCommandForPlatform', () => {
true
);
- expect(res.rpm).toMatchInlineSnapshot(`
- "sudo elastic-agent enroll --url=http://fleetserver:8220 \\\\
+ expect(res).toMatchInlineSnapshot(`
+ "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--x86_64.rpm \\\\
+ tar xzvf elastic-agent--x86_64.rpm \\\\
+ cd elastic-agent--x86_64 \\\\
+ sudo elastic-agent enroll --url=http://fleetserver:8220 \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1 \\\\
--fleet-server-policy=policy-1 \\\\
@@ -242,6 +317,7 @@ describe('getInstallCommandForPlatform', () => {
it('should return the correct command if the the policyId is set for deb', () => {
const res = getInstallCommandForPlatform(
+ 'deb',
'http://elasticsearch:9200',
'service-token-1',
'policy-1',
@@ -249,8 +325,11 @@ describe('getInstallCommandForPlatform', () => {
true
);
- expect(res.deb).toMatchInlineSnapshot(`
- "sudo elastic-agent enroll --url=http://fleetserver:8220 \\\\
+ expect(res).toMatchInlineSnapshot(`
+ "curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent--amd64.deb \\\\
+ tar xzvf elastic-agent--amd64.deb \\\\
+ cd elastic-agent--amd64 \\\\
+ sudo elastic-agent enroll --url=http://fleetserver:8220 \\\\
--fleet-server-es=http://elasticsearch:9200 \\\\
--fleet-server-service-token=service-token-1 \\\\
--fleet-server-policy=policy-1 \\\\
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts
new file mode 100644
index 0000000000000..525af7cf95103
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/utils/install_command_utils.ts
@@ -0,0 +1,119 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { PLATFORM_TYPE } from '../../../hooks';
+
+export type CommandsByPlatform = {
+ [key in PLATFORM_TYPE]: string;
+};
+
+function getArtifact(platform: PLATFORM_TYPE, kibanaVersion: string) {
+ const ARTIFACT_BASE_URL = 'https://artifacts.elastic.co/downloads/beats/elastic-agent';
+
+ const artifactMap: Record<
+ PLATFORM_TYPE,
+ { fullUrl: string; filename: string; unpackedDir: string }
+ > = {
+ linux: {
+ fullUrl: `${ARTIFACT_BASE_URL}/elastic-agent-${kibanaVersion}-linux-x86_64.zip`,
+ filename: `elastic-agent-${kibanaVersion}-linux-x86_64.zip`,
+ unpackedDir: `elastic-agent-${kibanaVersion}-linux-x86_64`,
+ },
+ mac: {
+ fullUrl: `${ARTIFACT_BASE_URL}/elastic-agent-${kibanaVersion}-darwin-x86_64.tar.gz`,
+ filename: `elastic-agent-${kibanaVersion}-darwin-x86_64.tar.gz`,
+ unpackedDir: `elastic-agent-${kibanaVersion}-darwin-x86_64`,
+ },
+ windows: {
+ fullUrl: `${ARTIFACT_BASE_URL}/elastic-agent-${kibanaVersion}-windows-x86_64.tar.gz`,
+ filename: `elastic-agent-${kibanaVersion}-windows-x86_64.tar.gz`,
+ unpackedDir: `elastic-agent-${kibanaVersion}-windows-x86_64`,
+ },
+ deb: {
+ fullUrl: `${ARTIFACT_BASE_URL}/elastic-agent-${kibanaVersion}-amd64.deb`,
+ filename: `elastic-agent-${kibanaVersion}-amd64.deb`,
+ unpackedDir: `elastic-agent-${kibanaVersion}-amd64`,
+ },
+ rpm: {
+ fullUrl: `${ARTIFACT_BASE_URL}/elastic-agent-${kibanaVersion}-x86_64.rpm`,
+ filename: `elastic-agent-${kibanaVersion}-x86_64.rpm`,
+ unpackedDir: `elastic-agent-${kibanaVersion}-x86_64`,
+ },
+ };
+
+ return artifactMap[platform];
+}
+
+export function getInstallCommandForPlatform(
+ platform: PLATFORM_TYPE,
+ esHost: string,
+ serviceToken: string,
+ policyId?: string,
+ fleetServerHost?: string,
+ isProductionDeployment?: boolean,
+ sslCATrustedFingerprint?: string,
+ kibanaVersion?: string
+): string {
+ const newLineSeparator = platform === 'windows' ? '`\n' : '\\\n';
+
+ const artifact = getArtifact(platform, kibanaVersion ?? '');
+ const downloadCommand =
+ platform === 'windows'
+ ? [
+ `wget ${artifact.fullUrl} -OutFile ${artifact.filename}`,
+ `Expand-Archive .\\${artifact.filename}`,
+ `cd ${artifact.unpackedDir}`,
+ ].join(` ${newLineSeparator}`)
+ : [
+ `curl -L -O ${artifact.fullUrl}`,
+ `tar xzvf ${artifact.filename}`,
+ `cd ${artifact.unpackedDir}`,
+ ].join(` ${newLineSeparator}`);
+
+ const commandArguments = [];
+
+ if (isProductionDeployment && fleetServerHost) {
+ commandArguments.push(['url', fleetServerHost]);
+ }
+
+ commandArguments.push(['fleet-server-es', esHost]);
+ commandArguments.push(['fleet-server-service-token', serviceToken]);
+ if (policyId) {
+ commandArguments.push(['fleet-server-policy', policyId]);
+ }
+
+ if (sslCATrustedFingerprint) {
+ commandArguments.push(['fleet-server-es-ca-trusted-fingerprint', sslCATrustedFingerprint]);
+ }
+
+ if (isProductionDeployment) {
+ commandArguments.push(['certificate-authorities', '']);
+ if (!sslCATrustedFingerprint) {
+ commandArguments.push(['fleet-server-es-ca', '']);
+ }
+ commandArguments.push(['fleet-server-cert', '']);
+ commandArguments.push(['fleet-server-cert-key', '']);
+ }
+
+ const commandArgumentsStr = commandArguments.reduce((acc, [key, val]) => {
+ if (acc === '' && key === 'url') {
+ return `--${key}=${val}`;
+ }
+ const valOrEmpty = val ? `=${val}` : '';
+ return (acc += ` ${newLineSeparator} --${key}${valOrEmpty}`);
+ }, '');
+
+ const commands = {
+ linux: `${downloadCommand} ${newLineSeparator}sudo ./elastic-agent install${commandArgumentsStr}`,
+ mac: `${downloadCommand} ${newLineSeparator}sudo ./elastic-agent install ${commandArgumentsStr}`,
+ windows: `${downloadCommand}${newLineSeparator}.\\elastic-agent.exe install ${commandArgumentsStr}`,
+ deb: `${downloadCommand} ${newLineSeparator}sudo elastic-agent enroll ${commandArgumentsStr}`,
+ rpm: `${downloadCommand} ${newLineSeparator}sudo elastic-agent enroll ${commandArgumentsStr}`,
+ };
+
+ return commands[platform];
+}
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/index.ts b/x-pack/plugins/fleet/public/applications/fleet/components/index.ts
index 5e927c5b0e3d6..a299013ab547e 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/components/index.ts
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/index.ts
@@ -8,3 +8,4 @@
export * from '../../../components';
export * from './search_bar';
+export * from './fleet_server_instructions';
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx
index 9fdcc0f73297f..e1c6bbafa21a7 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx
@@ -28,11 +28,11 @@ import { useStartServices } from '../../../../hooks';
import { AgentPolicyPackageBadge } from '../../../../components';
-import { policyHasFleetServer } from '../../../agents/services/has_fleet_server';
-
import { AgentPolicyDeleteProvider } from '../agent_policy_delete_provider';
import type { ValidationResults } from '../agent_policy_validation';
+import { policyHasFleetServer } from '../../../../services';
+
import { useOutputOptions, DEFAULT_OUTPUT_VALUE } from './hooks';
interface Props {
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx
index e9b04aacecb68..44e87d7fb4e63 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/actions_menu.tsx
@@ -18,8 +18,7 @@ import {
AgentUpgradeAgentModal,
} from '../../components';
import { useAgentRefresh } from '../hooks';
-import { isAgentUpgradeable } from '../../../../services';
-import { policyHasFleetServer } from '../../services/has_fleet_server';
+import { isAgentUpgradeable, policyHasFleetServer } from '../../../../services';
export const AgentDetailsActionMenu: React.FunctionComponent<{
agent: Agent;
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx
index 8e8091d13a794..c7a839bf3d594 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { useState, useMemo, useCallback, useRef, useEffect } from 'react';
+import React, { useState, useMemo, useCallback, useRef, useEffect, useContext } from 'react';
import {
EuiBasicTable,
EuiButton,
@@ -53,6 +53,8 @@ import {
} from '../components';
import { useFleetServerUnhealthy } from '../hooks/use_fleet_server_unhealthy';
+import { agentFlyoutContext } from '..';
+
import { AgentTableHeader } from './components/table_header';
import type { SelectionMode } from './components/bulk_actions';
import { SearchAndFilterBar } from './components/search_and_filter_bar';
@@ -203,6 +205,8 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
isOpen: false,
});
+ const flyoutContext = useContext(agentFlyoutContext);
+
// Agent actions states
const [agentToReassign, setAgentToReassign] = useState(undefined);
const [agentToUnenroll, setAgentToUnenroll] = useState(undefined);
@@ -380,11 +384,8 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
// Fleet server unhealthy status
const { isUnhealthy: isFleetServerUnhealthy } = useFleetServerUnhealthy();
const onClickAddFleetServer = useCallback(() => {
- setEnrollmentFlyoutState({
- isOpen: true,
- selectedPolicyId: agentPolicies.length > 0 ? agentPolicies[0].id : undefined,
- });
- }, [agentPolicies]);
+ flyoutContext?.openFleetServerFlyout();
+ }, [flyoutContext]);
const columns = [
{
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx
new file mode 100644
index 0000000000000..86990d84d5130
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx
@@ -0,0 +1,104 @@
+/*
+ * 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, { useContext } from 'react';
+import {
+ EuiButton,
+ EuiButtonEmpty,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiLink,
+ EuiSpacer,
+ EuiText,
+} from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n-react';
+
+import { useStartServices } from '../../../../hooks';
+
+import { agentFlyoutContext } from '../..';
+
+export const EnrollmentRecommendation: React.FunctionComponent<{
+ showStandaloneTab: () => void;
+}> = ({ showStandaloneTab }) => {
+ const flyoutContext = useContext(agentFlyoutContext);
+
+ const { docLinks } = useStartServices();
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+ ),
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx
deleted file mode 100644
index 93547bba36de8..0000000000000
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx
+++ /dev/null
@@ -1,748 +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, { useState, useMemo, useCallback, useEffect } from 'react';
-import {
- EuiButton,
- EuiFlexGroup,
- EuiFlexItem,
- EuiSpacer,
- EuiText,
- EuiLink,
- EuiSteps,
- EuiCode,
- EuiCodeBlock,
- EuiCallOut,
- EuiRadioGroup,
- EuiFieldText,
- EuiForm,
- EuiFormErrorText,
-} from '@elastic/eui';
-import type { EuiStepProps } from '@elastic/eui/src/components/steps/step';
-import styled from 'styled-components';
-import { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n-react';
-
-import { DownloadStep, SelectCreateAgentPolicy } from '../../../../components';
-import {
- useStartServices,
- useDefaultOutput,
- sendGenerateServiceToken,
- usePlatform,
- useGetAgentPolicies,
- useGetSettings,
- sendPutSettings,
- sendGetFleetStatus,
- useFleetStatus,
- useLink,
-} from '../../../../hooks';
-import type { PLATFORM_TYPE } from '../../../../hooks';
-import type { AgentPolicy } from '../../../../types';
-import { FleetServerOnPremRequiredCallout } from '../../components';
-
-import { policyHasFleetServer } from '../../services/has_fleet_server';
-
-import { PlatformSelector } from '../../../../../../components/enrollment_instructions/manual/platform_selector';
-
-import type { CommandsByPlatform } from './install_command_utils';
-import { getInstallCommandForPlatform } from './install_command_utils';
-
-const URL_REGEX = /^(https):\/\/[^\s$.?#].[^\s]*$/gm;
-const REFRESH_INTERVAL = 10000;
-
-type DeploymentMode = 'production' | 'quickstart';
-
-const FlexItemWithMinWidth = styled(EuiFlexItem)`
- min-width: 0px;
- max-width: 100%;
-`;
-
-export const ContentWrapper = styled(EuiFlexGroup)`
- height: 100%;
- margin: 0 auto;
- max-width: 800px;
-`;
-
-// Otherwise the copy button is over the text
-const CommandCode = styled.div.attrs(() => {
- return {
- className: 'eui-textBreakAll',
- };
-})`
- margin-right: ${(props) => props.theme.eui.paddingSizes.m};
-`;
-
-export const ServiceTokenStep = ({
- disabled = false,
- serviceToken,
- getServiceToken,
- isLoadingServiceToken,
-}: {
- disabled?: boolean;
- serviceToken?: string;
- getServiceToken: () => void;
- isLoadingServiceToken: boolean;
-}): EuiStepProps => {
- return {
- title: i18n.translate('xpack.fleet.fleetServerSetup.stepGenerateServiceTokenTitle', {
- defaultMessage: 'Generate a service token',
- }),
- status: disabled ? 'disabled' : undefined,
- children: !disabled && (
- <>
-
-
-
-
- {!serviceToken ? (
-
-
- {
- getServiceToken();
- }}
- data-test-subj="fleetServerGenerateServiceTokenBtn"
- >
-
-
-
-
- ) : (
- <>
-
- }
- />
-
-
-
-
-
-
-
-
-
- {serviceToken}
-
-
-
- >
- )}
- >
- ),
- };
-};
-
-export const FleetServerCommandStep = ({
- serviceToken,
- installCommand,
- platform,
- setPlatform,
-}: {
- serviceToken?: string;
- installCommand: CommandsByPlatform;
- platform: string;
- setPlatform: (platform: PLATFORM_TYPE) => void;
-}): EuiStepProps => {
- const { docLinks } = useStartServices();
-
- return {
- title: i18n.translate('xpack.fleet.fleetServerSetup.stepInstallAgentTitle', {
- defaultMessage: 'Start Fleet Server',
- }),
- status: !serviceToken ? 'disabled' : undefined,
- children: serviceToken ? (
- <>
-
-
-
-
- ),
- }}
- />
-
-
-
- >
- ) : null,
- };
-};
-
-export const useFleetServerInstructions = (policyId?: string) => {
- const { output } = useDefaultOutput();
- const { notifications } = useStartServices();
- const [serviceToken, setServiceToken] = useState();
- const [isLoadingServiceToken, setIsLoadingServiceToken] = useState(false);
- const { platform, setPlatform } = usePlatform();
- const [deploymentMode, setDeploymentMode] = useState('production');
- const { data: settings, resendRequest: refreshSettings } = useGetSettings();
- const fleetServerHost = settings?.item.fleet_server_hosts?.[0];
- const esHost = output?.hosts?.[0];
- const sslCATrustedFingerprint: string | undefined = output?.ca_trusted_fingerprint;
-
- const installCommand = useMemo((): CommandsByPlatform => {
- if (!serviceToken || !esHost) {
- return {
- linux: '',
- mac: '',
- windows: '',
- deb: '',
- rpm: '',
- };
- }
-
- return getInstallCommandForPlatform(
- esHost,
- serviceToken,
- policyId,
- fleetServerHost,
- deploymentMode === 'production',
- sslCATrustedFingerprint
- );
- }, [serviceToken, esHost, policyId, fleetServerHost, deploymentMode, sslCATrustedFingerprint]);
-
- const getServiceToken = useCallback(async () => {
- setIsLoadingServiceToken(true);
- try {
- const { data } = await sendGenerateServiceToken();
- if (data?.value) {
- setServiceToken(data?.value);
- }
- } catch (err) {
- notifications.toasts.addError(err, {
- title: i18n.translate('xpack.fleet.fleetServerSetup.errorGeneratingTokenTitleText', {
- defaultMessage: 'Error generating token',
- }),
- });
- }
-
- setIsLoadingServiceToken(false);
- }, [notifications.toasts]);
-
- const addFleetServerHost = useCallback(
- async (host: string) => {
- const res = await sendPutSettings({
- fleet_server_hosts: [host, ...(settings?.item.fleet_server_hosts || [])],
- });
- if (res.error) {
- throw res.error;
- }
- await refreshSettings();
- },
- [refreshSettings, settings?.item.fleet_server_hosts]
- );
-
- return {
- addFleetServerHost,
- fleetServerHost,
- deploymentMode,
- setDeploymentMode,
- serviceToken,
- getServiceToken,
- isLoadingServiceToken,
- installCommand,
- platform,
- setPlatform,
- };
-};
-
-const AgentPolicySelectionStep = ({
- selectedPolicy,
- setPolicyId,
- agentPolicies,
- refreshAgentPolicies,
-}: {
- selectedPolicy?: AgentPolicy;
- setPolicyId: (v?: string) => void;
- agentPolicies: AgentPolicy[];
- refreshAgentPolicies: () => void;
-}): EuiStepProps => {
- return {
- title:
- agentPolicies.length === 0
- ? i18n.translate('xpack.fleet.fleetServerSetup.stepCreateAgentPolicyTitle', {
- defaultMessage: 'Create an agent policy to host Fleet Server',
- })
- : i18n.translate('xpack.fleet.fleetServerSetup.stepSelectAgentPolicyTitle', {
- defaultMessage: 'Select an agent policy to host Fleet Server',
- }),
- status: undefined,
- children: (
- <>
-
- >
- ),
- };
-};
-
-export const addFleetServerHostStep = ({
- addFleetServerHost,
-}: {
- addFleetServerHost: (v: string) => Promise;
-}): EuiStepProps => {
- return {
- title: i18n.translate('xpack.fleet.fleetServerSetup.addFleetServerHostStepTitle', {
- defaultMessage: 'Add your Fleet Server host',
- }),
- status: undefined,
- children: ,
- };
-};
-
-export const AddFleetServerHostStepContent = ({
- addFleetServerHost,
-}: {
- addFleetServerHost: (v: string) => Promise;
-}) => {
- const [calloutHost, setCalloutHost] = useState();
- const [isLoading, setIsLoading] = useState(false);
- const [fleetServerHost, setFleetServerHost] = useState('');
- const [error, setError] = useState();
- const { notifications } = useStartServices();
-
- const { getHref } = useLink();
-
- const validate = useCallback(
- (host: string) => {
- if (host.match(URL_REGEX)) {
- setError(undefined);
- return true;
- } else {
- setError(
- i18n.translate('xpack.fleet.fleetServerSetup.addFleetServerHostInvalidUrlError', {
- defaultMessage: 'Valid https URL required.',
- })
- );
- return false;
- }
- },
- [setError]
- );
-
- const onSubmit = useCallback(async () => {
- try {
- setIsLoading(true);
- if (validate(fleetServerHost)) {
- await addFleetServerHost(fleetServerHost);
- setCalloutHost(fleetServerHost);
- setFleetServerHost('');
- } else {
- setCalloutHost('');
- }
- } catch (err) {
- notifications.toasts.addError(err, {
- title: i18n.translate('xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle', {
- defaultMessage: 'Error adding Fleet Server host',
- }),
- });
- } finally {
- setIsLoading(false);
- }
- }, [fleetServerHost, addFleetServerHost, validate, notifications.toasts]);
-
- const onChange = useCallback(
- (e: React.ChangeEvent) => {
- setFleetServerHost(e.target.value);
- if (error) {
- validate(e.target.value);
- }
- },
- [error, validate, setFleetServerHost]
- );
-
- return (
-
-
- 8220 }}
- />
-
-
-
-
-
-
-
- }
- data-test-subj="fleetServerHostInput"
- />
- {error && {error}}
-
-
-
-
-
-
-
- {calloutHost && (
- <>
-
-
- }
- >
-
-
-
- ),
- }}
- />
-
- >
- )}
-
- );
-};
-
-export const deploymentModeStep = ({
- deploymentMode,
- setDeploymentMode,
-}: {
- deploymentMode: DeploymentMode;
- setDeploymentMode: (v: DeploymentMode) => void;
-}): EuiStepProps => {
- return {
- title: i18n.translate('xpack.fleet.fleetServerSetup.stepDeploymentModeTitle', {
- defaultMessage: 'Choose a deployment mode for security',
- }),
- status: undefined,
- children: (
-
- ),
- };
-};
-
-const DeploymentModeStepContent = ({
- deploymentMode,
- setDeploymentMode,
-}: {
- deploymentMode: DeploymentMode;
- setDeploymentMode: (v: DeploymentMode) => void;
-}) => {
- const onChangeCallback = useCallback(
- (v: string) => {
- const value = v.split('_')[0];
- if (value === 'production' || value === 'quickstart') {
- setDeploymentMode(value);
- }
- },
- [setDeploymentMode]
- );
-
- // radio id has to be unique so that the component works even if appears twice in DOM (Agents tab, Add agent flyout)
- const radioSuffix = useMemo(() => Date.now(), []);
-
- return (
- <>
-
-
-
-
-
-
-
- ),
- }}
- />
- ),
- },
- {
- id: `production_${radioSuffix}`,
- label: (
-
-
-
- ),
- }}
- />
- ),
- },
- ]}
- idSelected={`${deploymentMode}_${radioSuffix}`}
- onChange={onChangeCallback}
- name={`radio group ${radioSuffix}`}
- />
- >
- );
-};
-
-const WaitingForFleetServerStep = ({
- status,
-}: {
- status: 'loading' | 'disabled' | 'complete';
-}): EuiStepProps => {
- return {
- title: i18n.translate('xpack.fleet.fleetServerSetup.stepWaitingForFleetServerTitle', {
- defaultMessage: 'Waiting for Fleet Server to connect...',
- }),
- status,
- children: undefined,
- };
-};
-
-const CompleteStep = (): EuiStepProps => {
- const fleetStatus = useFleetStatus();
-
- const onContinueClick = () => {
- fleetStatus.refresh();
- };
-
- return {
- title: i18n.translate('xpack.fleet.fleetServerSetup.stepFleetServerCompleteTitle', {
- defaultMessage: 'Fleet Server connected',
- }),
- status: 'complete',
- children: (
- <>
-
-
-
-
-
- >
- ),
- };
-};
-
-const findPolicyById = (policies: AgentPolicy[], id: string | undefined) => {
- if (!id) return undefined;
- return policies.find((p) => p.id === id);
-};
-
-export const OnPremInstructions: React.FC = () => {
- const { notifications } = useStartServices();
-
- const { data, resendRequest: refreshAgentPolicies } = useGetAgentPolicies({ full: true });
-
- const agentPolicies = useMemo(
- () => (data ? data.items.filter((item) => policyHasFleetServer(item)) : []),
- [data]
- );
-
- // Select default value
- let defaultValue = '';
- if (agentPolicies.length) {
- defaultValue = agentPolicies[0].id;
- }
- const [policyId, setPolicyId] = useState(defaultValue);
- const selectedPolicy = findPolicyById(agentPolicies, policyId);
-
- const {
- serviceToken,
- getServiceToken,
- isLoadingServiceToken,
- installCommand,
- platform,
- setPlatform,
- deploymentMode,
- setDeploymentMode,
- fleetServerHost,
- addFleetServerHost,
- } = useFleetServerInstructions(policyId);
-
- const { docLinks } = useStartServices();
-
- const [isWaitingForFleetServer, setIsWaitingForFleetServer] = useState(true);
-
- useEffect(() => {
- const interval = setInterval(async () => {
- try {
- const res = await sendGetFleetStatus();
- if (res.error) {
- throw res.error;
- }
- if (res.data?.isReady && !res.data?.missing_requirements?.includes('fleet_server')) {
- setIsWaitingForFleetServer(false);
- }
- } catch (err) {
- notifications.toasts.addError(err, {
- title: i18n.translate('xpack.fleet.fleetServerSetup.errorRefreshingFleetServerStatus', {
- defaultMessage: 'Error refreshing Fleet Server status',
- }),
- });
- }
- }, REFRESH_INTERVAL);
-
- return () => clearInterval(interval);
- }, [notifications.toasts]);
-
- return (
- <>
-
-
-
-
-
-
-
-
-
-
- ),
- }}
- />
-
-
-
- >
- );
-};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/index.tsx
index 3118fda75400f..293be6d8b98e4 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/index.tsx
@@ -6,4 +6,4 @@
*/
export { CloudInstructions } from './fleet_server_cloud_instructions';
-export * from './fleet_server_on_prem_instructions';
+export { EnrollmentRecommendation } from './enrollment_recommendation';
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils.ts
deleted file mode 100644
index b73eb547b6ddf..0000000000000
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils.ts
+++ /dev/null
@@ -1,65 +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 type { PLATFORM_TYPE } from '../../../../hooks';
-
-export type CommandsByPlatform = {
- [key in PLATFORM_TYPE]: string;
-};
-
-export function getInstallCommandForPlatform(
- esHost: string,
- serviceToken: string,
- policyId?: string,
- fleetServerHost?: string,
- isProductionDeployment?: boolean,
- sslCATrustedFingerprint?: string
-): CommandsByPlatform {
- const commandArguments: Array<[string, string] | [string]> = [];
-
- if (isProductionDeployment && fleetServerHost) {
- commandArguments.push(['url', fleetServerHost]);
- }
-
- commandArguments.push(['fleet-server-es', esHost]);
- commandArguments.push(['fleet-server-service-token', serviceToken]);
- if (policyId) {
- commandArguments.push(['fleet-server-policy', policyId]);
- }
-
- if (sslCATrustedFingerprint) {
- commandArguments.push(['fleet-server-es-ca-trusted-fingerprint', sslCATrustedFingerprint]);
- }
-
- if (isProductionDeployment) {
- commandArguments.push(['certificate-authorities', '']);
- if (!sslCATrustedFingerprint) {
- commandArguments.push(['fleet-server-es-ca', '']);
- }
- commandArguments.push(['fleet-server-cert', '']);
- commandArguments.push(['fleet-server-cert-key', '']);
- }
-
- const commandArgumentsStr = (platform?: string) => {
- const newLineSeparator = platform === 'windows' ? '`\n' : '\\\n';
- return commandArguments.reduce((acc, [key, val]) => {
- if (acc === '' && key === 'url') {
- return `--${key}=${val}`;
- }
- const valOrEmpty = val ? `=${val}` : '';
- return (acc += ` ${newLineSeparator} --${key}${valOrEmpty}`);
- }, '');
- };
-
- return {
- linux: `sudo ./elastic-agent install ${commandArgumentsStr()}`,
- mac: `sudo ./elastic-agent install ${commandArgumentsStr()}`,
- windows: `.\\elastic-agent.exe install ${commandArgumentsStr('windows')}`,
- deb: `sudo elastic-agent enroll ${commandArgumentsStr()}`,
- rpm: `sudo elastic-agent enroll ${commandArgumentsStr()}`,
- };
-}
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx
index bee7fbfd85371..b4818a6908adf 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx
@@ -13,9 +13,11 @@ import { useStartServices, sendGetPermissionsCheck } from '../../../hooks';
import { FleetServerMissingPrivileges } from '../components/fleet_server_callouts';
-import { Loading } from '../../../components';
+import { Loading } from '../components';
-import { CloudInstructions, OnPremInstructions } from './components';
+import { FleetServerInstructions } from '../../../components';
+
+import { CloudInstructions, EnrollmentRecommendation } from './components';
const FlexItemWithMinWidth = styled(EuiFlexItem)`
min-width: 0px;
@@ -27,7 +29,16 @@ const ContentWrapper = styled(EuiFlexGroup)`
margin: 0 auto;
`;
-export const FleetServerRequirementPage = () => {
+export const FleetServerRequirementPage: React.FunctionComponent<
+ | {
+ showEnrollmentRecommendation?: false;
+ showStandaloneTab?: never;
+ }
+ | {
+ showEnrollmentRecommendation?: true;
+ showStandaloneTab: () => void;
+ }
+> = ({ showStandaloneTab = () => {}, showEnrollmentRecommendation = true }) => {
const startService = useStartServices();
const deploymentUrl = startService.cloud?.deploymentUrl;
@@ -56,12 +67,7 @@ export const FleetServerRequirementPage = () => {
return (
<>
-
+
{deploymentUrl ? (
@@ -69,8 +75,10 @@ export const FleetServerRequirementPage = () => {
) : permissionsError ? (
+ ) : showEnrollmentRecommendation ? (
+
) : (
-
+
)}
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx
index d2434e109ca31..57da2fcf36d76 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.tsx
@@ -5,13 +5,13 @@
* 2.0.
*/
-import React, { useCallback, useEffect, useState } from 'react';
+import React, { createContext, useCallback, useEffect, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { Router, Route, Switch, useHistory } from 'react-router-dom';
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPortal } from '@elastic/eui';
import { FLEET_ROUTING_PATHS } from '../../constants';
-import { Loading, Error, AgentEnrollmentFlyout } from '../../components';
+import { Loading, Error, AgentEnrollmentFlyout, FleetServerFlyout } from '../../components';
import { useConfig, useFleetStatus, useBreadcrumbs, useAuthz, useGetSettings } from '../../hooks';
import { DefaultLayout, WithoutHeaderLayout } from '../../layouts';
@@ -21,6 +21,18 @@ import { AgentDetailsPage } from './agent_details_page';
import { NoAccessPage } from './error_pages/no_access';
import { FleetServerUpgradeModal } from './components/fleet_server_upgrade_modal';
+// TODO: Move all instances of toggling these flyouts to a global context object to avoid cases in which
+// we can render duplicate "stacked" flyouts
+export const agentFlyoutContext = createContext<
+ | {
+ openEnrollmentFlyout: () => void;
+ closeEnrollmentFlyout: () => void;
+ openFleetServerFlyout: () => void;
+ closeFleetServerFlyout: () => void;
+ }
+ | undefined
+>(undefined);
+
export const AgentsApp: React.FunctionComponent = () => {
useBreadcrumbs('agent_list');
const history = useHistory();
@@ -31,6 +43,8 @@ export const AgentsApp: React.FunctionComponent = () => {
const settings = useGetSettings();
const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false);
+ const [isFleetServerFlyoutOpen, setIsFleetServerFlyoutOpen] = useState(false);
+
const [fleetServerModalVisible, setFleetServerModalVisible] = useState(false);
const onCloseFleetServerModal = useCallback(() => {
setFleetServerModalVisible(false);
@@ -81,15 +95,6 @@ export const AgentsApp: React.FunctionComponent = () => {
const rightColumn = hasOnlyFleetServerMissingRequirement ? (
<>
- {isEnrollmentFlyoutOpen && (
-
- setIsEnrollmentFlyoutOpen(false)}
- />
-
- )}
{
) : undefined;
return (
-
-
-
-
-
-
-
- {fleetServerModalVisible && (
-
- )}
- {hasOnlyFleetServerMissingRequirement ? (
-
- ) : (
-
- )}
-
-
-
-
+ setIsEnrollmentFlyoutOpen(true),
+ closeEnrollmentFlyout: () => setIsEnrollmentFlyoutOpen(false),
+ openFleetServerFlyout: () => setIsFleetServerFlyoutOpen(true),
+ closeFleetServerFlyout: () => setIsFleetServerFlyoutOpen(false),
+ }}
+ >
+
+
+
+
+
+
+
+ {fleetServerModalVisible && (
+
+ )}
+ {hasOnlyFleetServerMissingRequirement ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {isEnrollmentFlyoutOpen && (
+
+ setIsEnrollmentFlyoutOpen(false)}
+ />
+
+ )}
+
+ {isFleetServerFlyoutOpen && (
+
+ setIsFleetServerFlyoutOpen(false)} />
+
+ )}
+
+
);
};
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 151a3d5354c17..aeb49928f3d1e 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
@@ -27,7 +27,7 @@ describe('useFleetServerHostsForm', () => {
const { result } = testRenderer.renderHook(() => useFleetServerHostsForm([], onSucess));
act(() =>
- result.current.fleetServerHostsInput.props.onChange(['http://test.fr', 'http://test.fr'])
+ result.current.fleetServerHostsInput.props.onChange(['https://test.fr', 'https://test.fr'])
);
await act(() => result.current.submit());
@@ -54,7 +54,7 @@ describe('useFleetServerHostsForm', () => {
testRenderer.startServices.http.post.mockResolvedValue({});
const { result } = testRenderer.renderHook(() => useFleetServerHostsForm([], onSucess));
- act(() => result.current.fleetServerHostsInput.props.onChange(['http://test.fr']));
+ act(() => result.current.fleetServerHostsInput.props.onChange(['https://test.fr']));
await act(() => result.current.submit());
expect(onSucess).toBeCalled();
@@ -67,14 +67,14 @@ describe('useFleetServerHostsForm', () => {
const { result } = testRenderer.renderHook(() => useFleetServerHostsForm([], onSucess));
act(() =>
- result.current.fleetServerHostsInput.props.onChange(['http://test.fr', 'http://test.fr'])
+ result.current.fleetServerHostsInput.props.onChange(['https://test.fr', 'https://test.fr'])
);
await act(() => result.current.submit());
expect(onSucess).not.toBeCalled();
expect(result.current.isDisabled).toBeTruthy();
- act(() => result.current.fleetServerHostsInput.props.onChange(['http://test.fr']));
+ act(() => result.current.fleetServerHostsInput.props.onChange(['https://test.fr']));
expect(result.current.isDisabled).toBeFalsy();
await act(() => result.current.submit());
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 ac196576ba889..b19e8ddda0427 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
@@ -15,7 +15,7 @@ import { isDiffPathProtocol } from '../../../../../../../common';
import { useConfirmModal } from '../../hooks/use_confirm_modal';
import { getAgentAndPolicyCount } from '../../services/agent_and_policies_count';
-const URL_REGEX = /^(https?):\/\/[^\s$.?#].[^\s]*$/gm;
+const URL_REGEX = /^(https):\/\/[^\s$.?#].[^\s]*$/gm;
const ConfirmTitle = () => (
{
return {
...module,
useGetSettings: jest.fn(),
- sendGetFleetStatus: jest.fn(),
sendGetOneAgentPolicy: jest.fn(),
useGetAgents: jest.fn(),
useGetAgentPolicies: jest.fn(),
@@ -36,14 +35,14 @@ jest.mock('../../applications/fleet/sections/agents/hooks/use_fleet_server_unhea
});
jest.mock(
- '../../applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions',
+ '../../applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form',
() => {
const module = jest.requireActual(
- '../../applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions'
+ '../../applications/fleet/components/fleet_server_instructions/hooks/use_advanced_form'
);
return {
...module,
- useFleetServerInstructions: jest.fn(),
+ useAdvancedForm: jest.fn(),
};
}
);
@@ -82,6 +81,9 @@ jest.mock('./steps', () => {
};
});
-jest.mock('../../applications/fleet/sections/agents/services/has_fleet_server', () => {
- return { policyHasFleetServer: jest.fn().mockReturnValue(true) };
+jest.mock('../../services', () => {
+ return {
+ ...jest.requireActual('../../services'),
+ policyHasFleetServer: jest.fn().mockReturnValue(true),
+ };
});
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 b2680129dd7d5..6e46ec90d5faf 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
@@ -25,8 +25,7 @@ import {
useFleetStatus,
} from '../../hooks';
-import { useFleetServerInstructions } from '../../applications/fleet/sections/agents/agent_requirements_page/components';
-
+import { useAdvancedForm } from '../../applications/fleet/components/fleet_server_instructions/hooks';
import { useFleetServerUnhealthy } from '../../applications/fleet/sections/agents/hooks/use_fleet_server_unhealthy';
import type { FlyOutProps } from './types';
@@ -44,8 +43,8 @@ const TestComponent = (props: FlyOutProps) => (
);
-const setup = async (props?: FlyOutProps) => {
- const testBed = await registerTestBed(TestComponent)(props);
+const setup = (props?: FlyOutProps) => {
+ const testBed = registerTestBed(TestComponent)(props);
const { find, component } = testBed;
return {
@@ -78,7 +77,7 @@ const testAgentPolicy: AgentPolicy = {
describe('', () => {
let testBed: TestBed;
- beforeEach(async () => {
+ beforeEach(() => {
(useGetSettings as jest.Mock).mockReturnValue({
data: { item: { fleet_server_hosts: ['test'] } },
});
@@ -93,13 +92,24 @@ describe('', () => {
data: { item: { package_policies: [] } },
});
- (useFleetServerInstructions as jest.Mock).mockReturnValue({
+ (useAdvancedForm as jest.Mock).mockReturnValue({
+ eligibleFleetServerPolicies: [{ name: 'test', id: 'test' }],
+ refreshEligibleFleetServerPolicies: jest.fn(),
+ fleetServerPolicyId: 'test',
+ setFleetServerPolicyId: jest.fn(),
+ isFleetServerReady: true,
serviceToken: 'test',
- getServiceToken: jest.fn(),
isLoadingServiceToken: false,
- installCommand: jest.fn(),
- platform: 'test',
- setPlatform: jest.fn(),
+ generateServiceToken: jest.fn(),
+ fleetServerHostForm: {
+ saveFleetServerHost: jest.fn(),
+ fleetServerHost: 'https://test.server:8220',
+ setFleetServerHost: jest.fn(),
+ error: '',
+ validateFleetServerHost: jest.fn(),
+ },
+ deploymentMode: 'quickstart',
+ setDeploymentMode: jest.fn(),
});
(useGetAgents as jest.Mock).mockReturnValue({
@@ -111,8 +121,8 @@ describe('', () => {
refreshAgentPolicies: jest.fn(),
});
- await act(async () => {
- testBed = await setup({
+ act(() => {
+ testBed = setup({
onClose: jest.fn(),
});
testBed.component.update();
@@ -123,15 +133,15 @@ describe('', () => {
jest.clearAllMocks();
});
- it('should show loading when agent policies are loading', async () => {
+ it('should show loading when agent policies are loading', () => {
(useAgentEnrollmentFlyoutData as jest.Mock).mockReturnValue?.({
agentPolicies: [],
refreshAgentPolicies: jest.fn(),
isLoadingInitialAgentPolicies: true,
});
- await act(async () => {
- testBed = await setup({
+ act(() => {
+ testBed = setup({
onClose: jest.fn(),
});
testBed.component.update();
@@ -143,7 +153,7 @@ describe('', () => {
});
describe('managed instructions', () => {
- it('uses the agent policy selection step', async () => {
+ it('uses the agent policy selection step', () => {
const { exists } = testBed;
expect(exists('agentEnrollmentFlyout')).toBe(true);
@@ -152,10 +162,10 @@ describe('', () => {
});
describe('with a specific policy', () => {
- beforeEach(async () => {
+ beforeEach(() => {
jest.clearAllMocks();
- await act(async () => {
- testBed = await setup({
+ act(() => {
+ testBed = setup({
agentPolicy: testAgentPolicy,
onClose: jest.fn(),
});
@@ -172,10 +182,10 @@ describe('', () => {
});
describe('with a specific policy when no agentPolicies set', () => {
- beforeEach(async () => {
+ beforeEach(() => {
jest.clearAllMocks();
- await act(async () => {
- testBed = await setup({
+ act(() => {
+ testBed = setup({
agentPolicy: testAgentPolicy,
onClose: jest.fn(),
});
@@ -189,41 +199,41 @@ describe('', () => {
expect(exists('agent-enrollment-key-selection-step')).toBe(true);
});
});
- });
- // Skipped due to UI changing in https://github.com/elastic/kibana/issues/125534. These tests should be rethought overall
- // to provide value around the new flyout structure
- describe.skip('standalone instructions', () => {
- it('uses the agent policy selection step', async () => {
- const { exists, actions } = testBed;
- actions.goToStandaloneTab();
+ // Skipped due to UI changing in https://github.com/elastic/kibana/issues/125534. These tests should be rethought overall
+ // to provide value around the new flyout structure
+ describe.skip('standalone instructions', () => {
+ it('uses the agent policy selection step', async () => {
+ const { exists, actions } = testBed;
+ actions.goToStandaloneTab();
- expect(exists('agentEnrollmentFlyout')).toBe(true);
- expect(exists('agent-policy-selection-step')).toBe(true);
- expect(exists('agent-enrollment-key-selection-step')).toBe(false);
- });
+ expect(exists('agentEnrollmentFlyout')).toBe(true);
+ expect(exists('agent-policy-selection-step')).toBe(true);
+ expect(exists('agent-enrollment-key-selection-step')).toBe(false);
+ });
- describe('with a specific policy', () => {
- beforeEach(async () => {
- jest.clearAllMocks();
- await act(async () => {
- testBed = await setup({
- agentPolicy: testAgentPolicy,
- onClose: jest.fn(),
+ describe('with a specific policy', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ act(() => {
+ testBed = setup({
+ agentPolicy: testAgentPolicy,
+ onClose: jest.fn(),
+ });
+ testBed.component.update();
});
- testBed.component.update();
});
- });
- it('does not use either of the agent policy selection or enrollment key steps', () => {
- const { exists, actions } = testBed;
- jest.clearAllMocks();
+ it('does not use either of the agent policy selection or enrollment key steps', () => {
+ const { exists, actions } = testBed;
+ jest.clearAllMocks();
- actions.goToStandaloneTab();
+ actions.goToStandaloneTab();
- expect(exists('agentEnrollmentFlyout')).toBe(true);
- expect(exists('agent-policy-selection-step')).toBe(false);
- expect(exists('agent-enrollment-key-selection-step')).toBe(false);
+ expect(exists('agentEnrollmentFlyout')).toBe(true);
+ expect(exists('agent-policy-selection-step')).toBe(false);
+ expect(exists('agent-enrollment-key-selection-step')).toBe(false);
+ });
});
});
});
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx
index 81ec1236920b8..304239ac77dad 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx
@@ -20,7 +20,7 @@ import { AgentPolicySelection } from '.';
interface Props {
agentPolicies: AgentPolicy[];
- selectedPolicy?: AgentPolicy;
+ selectedPolicyId?: string;
setSelectedPolicyId: (agentPolicyId?: string) => void;
excludeFleetServer?: boolean;
withKeySelection: boolean;
@@ -34,7 +34,7 @@ export const SelectCreateAgentPolicy: React.FC = ({
agentPolicies,
excludeFleetServer,
setSelectedPolicyId,
- selectedPolicy,
+ selectedPolicyId,
withKeySelection,
selectedApiKeyId,
onKeyChange,
@@ -111,7 +111,7 @@ export const SelectCreateAgentPolicy: React.FC = ({
onKeyChange={onKeyChange}
excludeFleetServer={excludeFleetServer}
onClickCreatePolicy={onClickCreatePolicy}
- selectedPolicy={selectedPolicy}
+ selectedPolicyId={selectedPolicyId}
setSelectedPolicyId={setSelectedPolicyId}
isFleetServerPolicy={isFleetServerPolicy}
/>
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx
index c5a6076308525..4c02ddeeaaf27 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.test.tsx
@@ -23,7 +23,7 @@ describe('step select agent policy', () => {
(renderResult = testRenderer.render(
void;
excludeFleetServer?: boolean;
onClickCreatePolicy: () => void;
@@ -50,23 +50,10 @@ type Props = {
}
);
-const resolveAgentId = (
- agentPolicies: AgentPolicy[],
- selectedAgentPolicyId?: string
-): undefined | string => {
- if (agentPolicies.length && !selectedAgentPolicyId) {
- if (agentPolicies.length === 1) {
- return agentPolicies[0].id;
- }
- }
-
- return selectedAgentPolicyId;
-};
-
export const AgentPolicySelection: React.FC = (props) => {
const {
agentPolicies,
- selectedPolicy,
+ selectedPolicyId,
setSelectedPolicyId,
excludeFleetServer,
onClickCreatePolicy,
@@ -75,17 +62,6 @@ export const AgentPolicySelection: React.FC = (props) => {
const hasFleetAllPrivileges = useAuthz().fleet.all;
- useEffect(
- function useDefaultAgentPolicyEffect() {
- const resolvedId = resolveAgentId(agentPolicies, selectedPolicy?.id);
- // find AgentPolicy
- if (resolvedId !== selectedPolicy?.id) {
- setSelectedPolicyId(resolvedId);
- }
- },
- [agentPolicies, setSelectedPolicyId, selectedPolicy]
- );
-
const onChangeCallback = (event: React.ChangeEvent) => {
const { value } = event.target;
setSelectedPolicyId(value);
@@ -144,7 +120,7 @@ export const AgentPolicySelection: React.FC = (props) => {
value: agentPolicy.id,
text: agentPolicy.name,
}))}
- value={selectedPolicy?.id}
+ value={selectedPolicyId}
onChange={onChangeCallback}
aria-label={i18n.translate(
'xpack.fleet.enrollmentStepAgentPolicy.policySelectAriaLabel',
@@ -152,28 +128,28 @@ export const AgentPolicySelection: React.FC = (props) => {
defaultMessage: 'Agent policy',
}
)}
- hasNoInitialSelection={!selectedPolicy?.id}
+ hasNoInitialSelection={!selectedPolicyId}
data-test-subj="agentPolicyDropdown"
- isInvalid={!selectedPolicy?.id}
+ isInvalid={!selectedPolicyId}
/>
- {selectedPolicy?.id && !isFleetServerPolicy && (
+ {selectedPolicyId && !isFleetServerPolicy && (
<>
>
)}
- {props.withKeySelection && props.onKeyChange && selectedPolicy?.id && (
+ {props.withKeySelection && props.onKeyChange && selectedPolicyId && (
<>
>
)}
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx
index 5e6ea53261cd8..7dba93e5ddd3e 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx
@@ -13,17 +13,19 @@ import { useFleetStatus, useGetAgents } from '../../hooks';
import { FleetServerRequirementPage } from '../../applications/fleet/sections/agents/agent_requirements_page';
-import { policyHasFleetServer } from '../../applications/fleet/sections/agents/services/has_fleet_server';
-
import { FLEET_SERVER_PACKAGE } from '../../constants';
import { useFleetServerUnhealthy } from '../../applications/fleet/sections/agents/hooks/use_fleet_server_unhealthy';
import { Loading } from '..';
+import { policyHasFleetServer } from '../../services';
+
+import { AdvancedTab } from '../../applications/fleet/components/fleet_server_instructions/advanced_tab';
+
import type { InstructionProps } from './types';
-import { ManagedSteps, StandaloneSteps, FleetServerSteps } from './steps';
+import { ManagedSteps, StandaloneSteps } from './steps';
import { DefaultMissingRequirements } from './default_missing_requirements';
export const Instructions = (props: InstructionProps) => {
@@ -35,6 +37,7 @@ export const Instructions = (props: InstructionProps) => {
selectionType,
setSelectionType,
mode,
+ setMode,
isIntegrationFlow,
} = props;
const fleetStatus = useFleetStatus();
@@ -86,7 +89,7 @@ export const Instructions = (props: InstructionProps) => {
if (mode === 'managed') {
if (showFleetServerEnrollment) {
- return ;
+ return setMode('standalone')} />;
} else if (showAgentEnrollment) {
return (
<>
@@ -101,11 +104,7 @@ export const Instructions = (props: InstructionProps) => {
>
)}
- {isFleetServerPolicySelected ? (
-
- ) : (
-
- )}
+ {isFleetServerPolicySelected ? : }
>
);
}
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_policy_selection_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_policy_selection_step.tsx
index c26bfd3f0e2b8..a0828bb72f489 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_policy_selection_step.tsx
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_policy_selection_step.tsx
@@ -40,7 +40,7 @@ export const AgentPolicySelectionStep = ({
<>
= ({
return ;
};
-
-export const FleetServerSteps: React.FunctionComponent = ({
- agentPolicy,
- agentPolicies,
- selectedPolicy,
- setSelectedPolicyId,
- refreshAgentPolicies,
-}) => {
- const [selectedApiKeyId, setSelectedAPIKeyId] = useState();
-
- const apiKey = useGetOneEnrollmentAPIKey(selectedApiKeyId);
- const apiKeyData = apiKey?.data;
- const fleetServerInstructions = useFleetServerInstructions(apiKeyData?.item?.policy_id);
-
- const fleetServerSteps = useMemo(() => {
- const {
- serviceToken,
- getServiceToken,
- isLoadingServiceToken,
- installCommand: managedInstallCommands,
- platform,
- setPlatform,
- deploymentMode,
- setDeploymentMode,
- addFleetServerHost,
- } = fleetServerInstructions;
-
- return [
- deploymentModeStep({ deploymentMode, setDeploymentMode }),
- addFleetServerHostStep({ addFleetServerHost }),
- ServiceTokenStep({ serviceToken, getServiceToken, isLoadingServiceToken }),
- FleetServerCommandStep({
- serviceToken,
- installCommand: managedInstallCommands,
- platform,
- setPlatform,
- }),
- ];
- }, [fleetServerInstructions]);
-
- const instructionsSteps = useMemo(() => {
- const steps: EuiContainedStepProps[] = !agentPolicy
- ? [
- AgentPolicySelectionStep({
- selectedPolicy,
- agentPolicies,
- selectedApiKeyId,
- setSelectedAPIKeyId,
- setSelectedPolicyId,
- refreshAgentPolicies,
- }),
- ]
- : [
- AgentEnrollmentKeySelectionStep({
- selectedPolicy,
- selectedApiKeyId,
- setSelectedAPIKeyId,
- }),
- ];
-
- steps.push(...fleetServerSteps);
-
- return steps;
- }, [
- agentPolicy,
- selectedPolicy,
- agentPolicies,
- selectedApiKeyId,
- setSelectedPolicyId,
- refreshAgentPolicies,
- fleetServerSteps,
- ]);
-
- return ;
-};
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx
index 59f6fdeafe727..bbb3d8d4794c3 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx
@@ -14,7 +14,7 @@ import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/st
import type { GetOneEnrollmentAPIKeyResponse } from '../../../../common/types/rest_spec/enrollment_api_key';
import { InstallSection } from '../../enrollment_instructions/install_section';
-import type { CommandsByPlatform } from '../../../applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils';
+import type { CommandsByPlatform } from '../../../applications/fleet/components/fleet_server_instructions/utils/install_command_utils';
import type { K8sMode } from '../types';
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_standalone_agent_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_standalone_agent_step.tsx
index fb6ddfd393dcc..74ce555f7c2e8 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_standalone_agent_step.tsx
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_standalone_agent_step.tsx
@@ -10,7 +10,8 @@ import { i18n } from '@kbn/i18n';
import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
-import type { CommandsByPlatform } from '../../../applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils';
+import type { CommandsByPlatform } from '../../../applications/fleet/components/fleet_server_instructions/utils/install_command_utils';
+
import { InstallSection } from '../../enrollment_instructions/install_section';
import type { K8sMode } from '../types';
diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/install_section.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/install_section.tsx
index 0cf43902db7e2..1ebe68b8c5282 100644
--- a/x-pack/plugins/fleet/public/components/enrollment_instructions/install_section.tsx
+++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/install_section.tsx
@@ -7,13 +7,12 @@
import React from 'react';
+import type { CommandsByPlatform } from '../../applications/fleet/components/fleet_server_instructions/utils';
+
import { InstallationMessage } from '../agent_enrollment_flyout/installation_message';
import type { K8sMode } from '../agent_enrollment_flyout/types';
-
-import type { CommandsByPlatform } from '../../applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils';
-
-import { PlatformSelector } from './manual/platform_selector';
+import { PlatformSelector } from '../platform_selector';
interface Props {
installCommand: CommandsByPlatform;
diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx
index 89c1dfe3cac37..75378cdc86378 100644
--- a/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx
+++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/standalone/index.tsx
@@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-import type { CommandsByPlatform } from '../../../applications/fleet/sections/agents/agent_requirements_page/components/install_command_utils';
+import type { CommandsByPlatform } from '../../../applications/fleet/components/fleet_server_instructions/utils/install_command_utils';
import type { K8sMode } from '../../agent_enrollment_flyout/types';
export const StandaloneInstructions = (
diff --git a/x-pack/plugins/fleet/public/components/index.ts b/x-pack/plugins/fleet/public/components/index.ts
index 9df4182bc8a4e..723b376699e07 100644
--- a/x-pack/plugins/fleet/public/components/index.ts
+++ b/x-pack/plugins/fleet/public/components/index.ts
@@ -23,3 +23,4 @@ export { AddAgentHelpPopover } from './add_agent_help_popover';
export { EuiButtonWithTooltip } from './eui_button_with_tooltip';
export * from './link_and_revision';
export * from './agent_enrollment_flyout';
+export * from './platform_selector';
diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/platform_selector.tsx b/x-pack/plugins/fleet/public/components/platform_selector.tsx
similarity index 96%
rename from x-pack/plugins/fleet/public/components/enrollment_instructions/manual/platform_selector.tsx
rename to x-pack/plugins/fleet/public/components/platform_selector.tsx
index e03e43907f829..ae18f56b4b3ac 100644
--- a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/platform_selector.tsx
+++ b/x-pack/plugins/fleet/public/components/platform_selector.tsx
@@ -10,8 +10,8 @@ import styled from 'styled-components';
import { EuiSpacer, EuiCodeBlock, EuiButtonGroup, EuiCallOut } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import type { PLATFORM_TYPE } from '../../../hooks';
-import { PLATFORM_OPTIONS, usePlatform } from '../../../hooks';
+import type { PLATFORM_TYPE } from '../hooks';
+import { PLATFORM_OPTIONS, usePlatform } from '../hooks';
interface Props {
linuxCommand: string;
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/has_fleet_server.ts b/x-pack/plugins/fleet/public/services/has_fleet_server.ts
similarity index 79%
rename from x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/has_fleet_server.ts
rename to x-pack/plugins/fleet/public/services/has_fleet_server.ts
index c10049303234c..e1100d6447aa2 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/has_fleet_server.ts
+++ b/x-pack/plugins/fleet/public/services/has_fleet_server.ts
@@ -5,8 +5,8 @@
* 2.0.
*/
-import type { AgentPolicy, PackagePolicy } from '../../../types';
-import { FLEET_SERVER_PACKAGE } from '../../../constants';
+import { FLEET_SERVER_PACKAGE } from '../constants';
+import type { AgentPolicy, PackagePolicy } from '../types';
export function policyHasFleetServer(agentPolicy: AgentPolicy) {
return agentPolicy.package_policies?.some(
diff --git a/x-pack/plugins/fleet/public/services/index.ts b/x-pack/plugins/fleet/public/services/index.ts
index 306b081dce6c4..9de918d01d707 100644
--- a/x-pack/plugins/fleet/public/services/index.ts
+++ b/x-pack/plugins/fleet/public/services/index.ts
@@ -44,3 +44,4 @@ export {
export * from './pkg_key_from_package_info';
export * from './ui_extensions';
export * from './increment_policy_name';
+export * from './has_fleet_server';
diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json
index 43b4cbb3b2496..b5ee5b84642bf 100644
--- a/x-pack/plugins/translations/translations/fr-FR.json
+++ b/x-pack/plugins/translations/translations/fr-FR.json
@@ -12792,7 +12792,6 @@
"xpack.fleet.fleetServerSetup.cloudDeploymentLink": "Modifier le déploiement",
"xpack.fleet.fleetServerSetup.cloudSetupText": "Un serveur Fleet est nécessaire pour enregistrer des agents avec Fleet. Le moyen le plus simple d’en obtenir un est d’ajouter un serveur d’intégration, qui prend en charge l’intégration du serveur Fleet. Vous pouvez l’ajouter à votre déploiement dans la console cloud. Pour en savoir plus, consultez {link}",
"xpack.fleet.fleetServerSetup.cloudSetupTitle": "Activer un serveur Fleet",
- "xpack.fleet.fleetServerSetup.continueButton": "Continuer",
"xpack.fleet.fleetServerSetup.deploymentModeProductionOption": "{production} : fournissez vos propres certificats. Cette option demande aux agents de préciser une clé de certificat lors de leur enregistrement avec Fleet",
"xpack.fleet.fleetServerSetup.deploymentModeQuickStartOption": "{quickStart} : le serveur Fleet génère un certificat autosigné. Les agents suivants doivent être enregistrés avec l'indicateur --insecure. Non recommandé pour les cas d'utilisation en production.",
"xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle": "Erreur lors de l'ajout de l'hôte du serveur Fleet",
@@ -12801,23 +12800,16 @@
"xpack.fleet.fleetServerSetup.fleetSettingsLink": "Paramètres de Fleet",
"xpack.fleet.fleetServerSetup.generateServiceTokenButton": "Générer un jeton de service",
"xpack.fleet.fleetServerSetup.generateServiceTokenDescription": "Un jeton de service accorde au serveur Fleet les autorisations en écriture nécessaires dans Elasticsearch.",
- "xpack.fleet.fleetServerSetup.installAgentDescription": "Depuis le répertoire de l'agent, copiez et exécutez la commande de démarrage rapide appropriée pour lancer un agent Elastic en tant que serveur Fleet à l'aide du jeton généré et d'un certificat autosigné. Reportez-vous au {userGuideLink} pour obtenir les instructions d'utilisation de vos propres certificats à des fins de déploiement de production. Toutes les commandes nécessitent des privilèges d'administrateur.",
"xpack.fleet.fleetServerSetup.productionText": "Production",
"xpack.fleet.fleetServerSetup.quickStartText": "Démarrage rapide",
"xpack.fleet.fleetServerSetup.saveServiceTokenDescription": "Enregistrez les informations de votre jeton de service. Ce message s'affiche une seule fois.",
"xpack.fleet.fleetServerSetup.serviceTokenLabel": "Jeton de service",
"xpack.fleet.fleetServerSetup.setupGuideLink": "Guide de Fleet et d’Elastic Agent",
- "xpack.fleet.fleetServerSetup.setupText": "Un serveur Fleet est nécessaire pour enregistrer des agents avec Fleet. Suivez les instructions ci-après pour configurer un serveur Fleet. Pour en savoir plus, consultez le {userGuideLink}.",
- "xpack.fleet.fleetServerSetup.setupTitle": "Ajouter un serveur Fleet",
"xpack.fleet.fleetServerSetup.stepCreateAgentPolicyTitle": "Créer une stratégie d’agent pour héberger le serveur Fleet",
"xpack.fleet.fleetServerSetup.stepDeploymentModeDescriptionText": "Fleet utilise le protocole TLS (Transport Layer Security) pour chiffrer le trafic entre les agents Elastic et d'autres composants de la Suite Elastic. Sélectionnez un mode de déploiement pour définir comment vous souhaitez gérer les certificats. Votre sélection impactera la commande de configuration du serveur Fleet qui s'affichera à une étape ultérieure.",
"xpack.fleet.fleetServerSetup.stepDeploymentModeTitle": "Sélectionner un mode de déploiement pour Security",
- "xpack.fleet.fleetServerSetup.stepFleetServerCompleteDescription": "Vous pouvez désormais enregistrer des agents avec Fleet.",
- "xpack.fleet.fleetServerSetup.stepFleetServerCompleteTitle": "Serveur Fleet connecté",
"xpack.fleet.fleetServerSetup.stepGenerateServiceTokenTitle": "Générer un jeton de service",
- "xpack.fleet.fleetServerSetup.stepInstallAgentTitle": "Lancer le serveur Fleet",
"xpack.fleet.fleetServerSetup.stepSelectAgentPolicyTitle": "Sélectionner une stratégie d’agent pour héberger le serveur Fleet",
- "xpack.fleet.fleetServerSetup.stepWaitingForFleetServerTitle": "En attente de connexion du serveur Fleet…",
"xpack.fleet.fleetServerSetup.waitingText": "En attente de connexion d'un serveur Fleet…",
"xpack.fleet.fleetServerSetupPermissionDeniedErrorMessage": "Le serveur Fleet doit être configuré. Pour cela, le privilège de cluster {roleName} est requis. Contactez votre administrateur.",
"xpack.fleet.fleetServerSetupPermissionDeniedErrorTitle": "Autorisation refusée",
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 520061476f96d..4e4aa106a9b67 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -12912,7 +12912,6 @@
"xpack.fleet.fleetServerSetup.cloudDeploymentLink": "デプロイを編集",
"xpack.fleet.fleetServerSetup.cloudSetupText": "Fleetにエージェントを登録する前に、Fleetサーバーが必要です。取得するための最も簡単な方法は、Fleetサーバー統合を実行する統合サーバーを追加することです。クラウドコンソールでデプロイに追加できます。詳細は{link}をご覧ください。",
"xpack.fleet.fleetServerSetup.cloudSetupTitle": "Fleetサーバーを有効にする",
- "xpack.fleet.fleetServerSetup.continueButton": "続行",
"xpack.fleet.fleetServerSetup.deploymentModeProductionOption": "{production} – 独自の証明書を指定します。このオプションでは、Fleetに登録するときに、エージェントで証明書鍵を指定する必要があります。",
"xpack.fleet.fleetServerSetup.deploymentModeQuickStartOption": "{quickStart} – Fleetサーバーは自己署名証明書を生成します。後続のエージェントは--insecureフラグを使用して登録する必要があります。本番ユースケースには推奨されません。",
"xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle": "Fleetサーバーホストの追加エラー",
@@ -12921,23 +12920,16 @@
"xpack.fleet.fleetServerSetup.fleetSettingsLink": "Fleet設定",
"xpack.fleet.fleetServerSetup.generateServiceTokenButton": "サービストークンを生成",
"xpack.fleet.fleetServerSetup.generateServiceTokenDescription": "サービストークンは、Elasticsearchに書き込むためのFleetサーバーアクセス権を付与します。",
- "xpack.fleet.fleetServerSetup.installAgentDescription": "エージェントディレクトリから、適切なクイックスタートコマンドをコピーして実行し、生成されたトークンと自己署名証明書を使用して、ElasticエージェントをFleetサーバーとして起動します。本番デプロイで独自の証明書を使用する手順については、{userGuideLink}を参照してください。すべてのコマンドには管理者権限が必要です。",
"xpack.fleet.fleetServerSetup.productionText": "本番運用",
"xpack.fleet.fleetServerSetup.quickStartText": "クイックスタート",
"xpack.fleet.fleetServerSetup.saveServiceTokenDescription": "サービストークン情報を保存します。これは1回だけ表示されます。",
"xpack.fleet.fleetServerSetup.serviceTokenLabel": "サービストークン",
"xpack.fleet.fleetServerSetup.setupGuideLink": "FleetおよびElasticエージェントガイド",
- "xpack.fleet.fleetServerSetup.setupText": "Fleetにエージェントを登録する前に、Fleetサーバーが必要です。Fleetサーバーのセットアップについては、次の手順に従ってください。詳細については、{userGuideLink}を参照してください。",
- "xpack.fleet.fleetServerSetup.setupTitle": "Fleetサーバーを追加",
"xpack.fleet.fleetServerSetup.stepCreateAgentPolicyTitle": "Fleetサーバーをホストするエージェントポリシーを作成",
"xpack.fleet.fleetServerSetup.stepDeploymentModeDescriptionText": "FleetはTransport Layer Security(TLS)を使用して、ElasticエージェントとElastic Stackの他のコンポーネントとの間の通信を暗号化します。デプロイモードを選択し、証明書を処理する方法を決定します。選択内容は後続のステップに表示されるFleetサーバーセットアップコマンドに影響します。",
"xpack.fleet.fleetServerSetup.stepDeploymentModeTitle": "セキュリティのデプロイモードを選択",
- "xpack.fleet.fleetServerSetup.stepFleetServerCompleteDescription": "エージェントをFleetに登録できます。",
- "xpack.fleet.fleetServerSetup.stepFleetServerCompleteTitle": "Fleetサーバーが接続されました",
"xpack.fleet.fleetServerSetup.stepGenerateServiceTokenTitle": "サービストークンを生成",
- "xpack.fleet.fleetServerSetup.stepInstallAgentTitle": "Fleetサーバーを起動",
"xpack.fleet.fleetServerSetup.stepSelectAgentPolicyTitle": "Fleetサーバーをホストするエージェントポリシーを選択",
- "xpack.fleet.fleetServerSetup.stepWaitingForFleetServerTitle": "Fleetサーバーの接続を待機しています...",
"xpack.fleet.fleetServerSetup.waitingText": "Fleetサーバーの接続を待機しています...",
"xpack.fleet.fleetServerSetupPermissionDeniedErrorMessage": "Fleetサーバーを設定する必要があります。これには{roleName}クラスター権限が必要です。管理者にお問い合わせください。",
"xpack.fleet.fleetServerSetupPermissionDeniedErrorTitle": "パーミッションが拒否されました",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 444d5e9db060b..19954e2f91320 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -12936,7 +12936,6 @@
"xpack.fleet.fleetServerSetup.cloudDeploymentLink": "编辑部署",
"xpack.fleet.fleetServerSetup.cloudSetupText": "需要提供 Fleet 服务器,才能使用 Fleet 注册代理。获取 Fleet 服务器的最简单方法是添加集成服务器,它会运行 Fleet 服务器集成。您可以在云控制台中将其添加到部署中。有关更多信息,请参阅{link}",
"xpack.fleet.fleetServerSetup.cloudSetupTitle": "启用 Fleet 服务器",
- "xpack.fleet.fleetServerSetup.continueButton": "继续",
"xpack.fleet.fleetServerSetup.deploymentModeProductionOption": "{production} – 提供您自己的证书。注册到 Fleet 时,此选项将需要代理指定证书密钥",
"xpack.fleet.fleetServerSetup.deploymentModeQuickStartOption": "{quickStart} – Fleet 服务器将生成自签名证书。必须使用 --insecure 标志注册后续代理。不推荐用于生产用例。",
"xpack.fleet.fleetServerSetup.errorAddingFleetServerHostTitle": "添加 Fleet 服务器主机时出错",
@@ -12945,23 +12944,16 @@
"xpack.fleet.fleetServerSetup.fleetSettingsLink": "Fleet 设置",
"xpack.fleet.fleetServerSetup.generateServiceTokenButton": "生成服务令牌",
"xpack.fleet.fleetServerSetup.generateServiceTokenDescription": "服务令牌授予 Fleet 服务器向 Elasticsearch 写入的权限。",
- "xpack.fleet.fleetServerSetup.installAgentDescription": "从代理目录中,复制并运行适当的快速启动命令,以使用生成的令牌和自签名证书将 Elastic 代理启动为 Fleet 服务器。有关如何将自己的证书用于生产部署,请参阅 {userGuideLink}。所有命令都需要管理员权限。",
"xpack.fleet.fleetServerSetup.productionText": "生产",
"xpack.fleet.fleetServerSetup.quickStartText": "快速启动",
"xpack.fleet.fleetServerSetup.saveServiceTokenDescription": "保存服务令牌信息。其仅显示一次。",
"xpack.fleet.fleetServerSetup.serviceTokenLabel": "服务令牌",
"xpack.fleet.fleetServerSetup.setupGuideLink": "Fleet 和 Elastic 代理指南",
- "xpack.fleet.fleetServerSetup.setupText": "需要提供 Fleet 服务器,才能使用 Fleet 注册代理。按照下面的说明设置 Fleet 服务器。有关详细信息,请参阅{userGuideLink}。",
- "xpack.fleet.fleetServerSetup.setupTitle": "添加 Fleet 服务器",
"xpack.fleet.fleetServerSetup.stepCreateAgentPolicyTitle": "创建代理策略来托管 Fleet 服务器",
"xpack.fleet.fleetServerSetup.stepDeploymentModeDescriptionText": "Fleet 使用传输层安全 (TLS) 加密 Elastic 代理和 Elastic Stack 中的其他组件之间的流量。选择部署模式来决定处理证书的方式。您的选择将影响后面步骤中显示的 Fleet 服务器设置命令。",
"xpack.fleet.fleetServerSetup.stepDeploymentModeTitle": "为安全选择部署模式",
- "xpack.fleet.fleetServerSetup.stepFleetServerCompleteDescription": "现在可以将代理注册到 Fleet。",
- "xpack.fleet.fleetServerSetup.stepFleetServerCompleteTitle": "Fleet 服务器已连接",
"xpack.fleet.fleetServerSetup.stepGenerateServiceTokenTitle": "生成服务令牌",
- "xpack.fleet.fleetServerSetup.stepInstallAgentTitle": "启动 Fleet 服务器",
"xpack.fleet.fleetServerSetup.stepSelectAgentPolicyTitle": "选择代理策略来托管 Fleet 服务器",
- "xpack.fleet.fleetServerSetup.stepWaitingForFleetServerTitle": "正在等待 Fleet 服务器连接......",
"xpack.fleet.fleetServerSetup.waitingText": "等候 Fleet 服务器连接......",
"xpack.fleet.fleetServerSetupPermissionDeniedErrorMessage": "需要设置 Fleet 服务器。这需要 {roleName} 集群权限。请联系您的管理员。",
"xpack.fleet.fleetServerSetupPermissionDeniedErrorTitle": "权限被拒绝",