Skip to content

Commit

Permalink
[Fleet] Fix syncing issue in Agent flyout (#135734)
Browse files Browse the repository at this point in the history
* [Fleet] Fix syncing issue in Agent flyout

* Use context based solution instead of polling

* Add clarifying comment

* Fix types

Co-authored-by: Kyle Pollich <[email protected]>
  • Loading branch information
criamico and kpollich authored Jul 5, 2022
1 parent 5d85008 commit b8dcdea
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<typeof setInterval> | 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, {
Expand All @@ -55,7 +54,7 @@ export const useWaitForFleetServer = () => {
};

return cleanup;
}, [notifications.toasts, isFleetServerReady]);
}, [notifications.toasts, isFleetServerReady, fleetStatus]);

return { isFleetServerReady };
};
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 ? (
<>
Expand All @@ -53,7 +59,7 @@ const ConfirmFleetServerConnectionStepContent: React.FunctionComponent<{

<EuiSpacer size="m" />

<EuiButton color="primary" onClick={flyoutContext.openEnrollmentFlyout}>
<EuiButton color="primary" onClick={handleContinueClick}>
<FormattedMessage
id="xpack.fleet.fleetServerFlyout.continueEnrollingButton"
defaultMessage="Continue enrolling Elastic Agent"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ describe('AgentApp', () => {
enabled: true,
isReady: false,
refresh: async () => {},
forceDisplayInstructions: false,
setForceDisplayInstructions: () => {},
});
const { utils } = renderAgentsApp();

Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -109,6 +115,8 @@ describe('AgentApp', () => {
missingRequirements: [],
missingOptionalFeatures: ['encrypted_saved_object_encryption_key_required'],
refresh: async () => {},
forceDisplayInstructions: false,
setForceDisplayInstructions: () => {},
});
const { utils } = renderAgentsApp();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand All @@ -86,7 +89,7 @@ export const AgentsApp: React.FunctionComponent = () => {
return <NoAccessPage />;
}

const rightColumn = hasOnlyFleetServerMissingRequirement ? (
const rightColumn = displayInstructions ? (
<>
<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false}>
Expand Down Expand Up @@ -114,7 +117,7 @@ export const AgentsApp: React.FunctionComponent = () => {
{fleetServerModalVisible && (
<FleetServerUpgradeModal onClose={onCloseFleetServerModal} />
)}
{hasOnlyFleetServerMissingRequirement ? (
{displayInstructions ? (
<FleetServerRequirementPage showEnrollmentRecommendation={false} />
) : (
<AgentListPage />
Expand Down
13 changes: 12 additions & 1 deletion x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,26 @@ interface FleetStatusState {

interface FleetStatus extends FleetStatusState {
refresh: () => Promise<void>;

// 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<boolean>;
}

const FleetStatusContext = React.createContext<FleetStatus | undefined>(undefined);

export const FleetStatusProvider: React.FC = ({ children }) => {
const config = useConfig();
const [forceDisplayInstructions, setForceDisplayInstructions] = useState(false);

const [state, setState] = useState<FleetStatusState>({
enabled: config.agents.enabled,
isLoading: false,
isReady: false,
});

const sendGetStatus = useCallback(
async function sendGetStatus() {
try {
Expand Down Expand Up @@ -64,7 +73,9 @@ export const FleetStatusProvider: React.FC = ({ children }) => {
const refresh = useCallback(() => sendGetStatus(), [sendGetStatus]);

return (
<FleetStatusContext.Provider value={{ ...state, refresh }}>
<FleetStatusContext.Provider
value={{ ...state, refresh, forceDisplayInstructions, setForceDisplayInstructions }}
>
{children}
</FleetStatusContext.Provider>
);
Expand Down

0 comments on commit b8dcdea

Please sign in to comment.