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 index 4da59560e408e..7721025a75067 100644 --- 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 @@ -6,9 +6,9 @@ */ import { i18n } from '@kbn/i18n'; -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; -import { sendGetFleetStatus, useStartServices } from '../../../hooks'; +import { useFleetStatus, useStartServices } from '../../../hooks'; const REFRESH_INTERVAL = 10000; @@ -17,26 +17,25 @@ const REFRESH_INTERVAL = 10000; * in the `missing_requirements` list. */ export const useWaitForFleetServer = () => { - const [isFleetServerReady, setIsFleetServerReady] = useState(false); + const fleetStatus = useFleetStatus(); const { notifications } = useStartServices(); + const isFleetServerReady = + fleetStatus.isReady && !fleetStatus.missingRequirements?.includes('fleet_server'); + 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 (isFleetServerReady) { if (interval) { clearInterval(interval); } + } else { + fleetStatus.setForceDisplayInstructions(true); + fleetStatus.refresh(); } } catch (err) { notifications.toasts.addError(err, { @@ -55,7 +54,7 @@ export const useWaitForFleetServer = () => { }; return cleanup; - }, [notifications.toasts, isFleetServerReady]); + }, [notifications.toasts, isFleetServerReady, fleetStatus]); return { isFleetServerReady }; }; 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 index 15e8609191019..8111fd3f09ac6 100644 --- 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 @@ -13,7 +13,7 @@ import { EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useFlyoutContext } from '../../../hooks'; +import { useFleetStatus, useFlyoutContext } from '../../../hooks'; export function getConfirmFleetServerConnectionStep({ disabled, @@ -41,6 +41,12 @@ const ConfirmFleetServerConnectionStepContent: React.FunctionComponent<{ isFleetServerReady: boolean; }> = ({ isFleetServerReady }) => { const flyoutContext = useFlyoutContext(); + const fleetStatus = useFleetStatus(); + + const handleContinueClick = () => { + fleetStatus.forceDisplayInstructions = false; + flyoutContext.openEnrollmentFlyout(); + }; return isFleetServerReady ? ( <> @@ -53,7 +59,7 @@ const ConfirmFleetServerConnectionStepContent: React.FunctionComponent<{ - + { enabled: true, isReady: false, refresh: async () => {}, + forceDisplayInstructions: false, + setForceDisplayInstructions: () => {}, }); const { utils } = renderAgentsApp(); @@ -82,6 +84,8 @@ describe('AgentApp', () => { isReady: false, missingRequirements: ['api_keys'], refresh: async () => {}, + forceDisplayInstructions: false, + setForceDisplayInstructions: () => {}, }); const { utils } = renderAgentsApp(); expect(utils.queryByText('MissingESRequirementsPage')).not.toBeNull(); @@ -95,6 +99,8 @@ describe('AgentApp', () => { isReady: false, missingRequirements: ['fleet_server'], refresh: async () => {}, + forceDisplayInstructions: false, + setForceDisplayInstructions: () => {}, }); const { utils } = renderAgentsApp(); expect(utils.queryByText('FleetServerRequirementPage')).not.toBeNull(); @@ -109,6 +115,8 @@ describe('AgentApp', () => { missingRequirements: [], missingOptionalFeatures: ['encrypted_saved_object_encryption_key_required'], refresh: async () => {}, + forceDisplayInstructions: false, + setForceDisplayInstructions: () => {}, }); const { utils } = renderAgentsApp(); 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 78ceb6293d3ce..ed770311ad9e9 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 @@ -75,6 +75,9 @@ export const AgentsApp: React.FunctionComponent = () => { fleetStatus?.missingRequirements?.length === 1 && fleetStatus.missingRequirements[0] === 'fleet_server'; + const displayInstructions = + fleetStatus.forceDisplayInstructions || hasOnlyFleetServerMissingRequirement; + if ( !hasOnlyFleetServerMissingRequirement && fleetStatus.missingRequirements && @@ -86,7 +89,7 @@ export const AgentsApp: React.FunctionComponent = () => { return ; } - const rightColumn = hasOnlyFleetServerMissingRequirement ? ( + const rightColumn = displayInstructions ? ( <> @@ -114,7 +117,7 @@ export const AgentsApp: React.FunctionComponent = () => { {fleetServerModalVisible && ( )} - {hasOnlyFleetServerMissingRequirement ? ( + {displayInstructions ? ( ) : ( diff --git a/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx b/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx index b05df0c619e0a..7319cd9f07be7 100644 --- a/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx +++ b/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx @@ -23,17 +23,26 @@ interface FleetStatusState { interface FleetStatus extends FleetStatusState { refresh: () => Promise; + + // This flag allows us to opt into displaying the Fleet Server enrollment instructions even if + // a healthy Fleet Server has been detected, so we can delay removing the enrollment UI until + // some user action like clicking a "continue" button + forceDisplayInstructions: boolean; + setForceDisplayInstructions: React.Dispatch; } const FleetStatusContext = React.createContext(undefined); export const FleetStatusProvider: React.FC = ({ children }) => { const config = useConfig(); + const [forceDisplayInstructions, setForceDisplayInstructions] = useState(false); + const [state, setState] = useState({ enabled: config.agents.enabled, isLoading: false, isReady: false, }); + const sendGetStatus = useCallback( async function sendGetStatus() { try { @@ -64,7 +73,9 @@ export const FleetStatusProvider: React.FC = ({ children }) => { const refresh = useCallback(() => sendGetStatus(), [sendGetStatus]); return ( - + {children} );