From b4855ba793c414fa5225f8ce1d6754a3ab20bcac Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Mon, 14 Feb 2022 17:55:59 +0100 Subject: [PATCH] [Fleet] refresh policies on create and flyout open (#125499) * refresh policies on create and flyout open * fix checks * fixed scenario when newly created policy should be preselected --- .../agent_policy_select_create.tsx | 4 +- .../agent_policy_selection.tsx | 2 +- .../agent_enrollment_flyout/index.tsx | 35 +- .../managed_instructions.tsx | 30 +- .../standalone_instructions.tsx | 429 +++++++++--------- .../agent_enrollment_flyout/steps.tsx | 16 +- .../agent_enrollment_flyout/types.ts | 4 + 7 files changed, 275 insertions(+), 245 deletions(-) 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 80ab845aaa49..87382ac70a9b 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 @@ -25,6 +25,7 @@ interface Props { selectedApiKeyId?: string; onKeyChange?: (key?: string) => void; isFleetServerPolicy?: boolean; + policyId?: string; } export const SelectCreateAgentPolicy: React.FC = ({ @@ -35,6 +36,7 @@ export const SelectCreateAgentPolicy: React.FC = ({ selectedApiKeyId, onKeyChange, isFleetServerPolicy, + policyId, }) => { const [showCreatePolicy, setShowCreatePolicy] = useState(agentPolicies.length === 0); @@ -42,7 +44,7 @@ export const SelectCreateAgentPolicy: React.FC = ({ const [newName, setNewName] = useState(incrementPolicyName(agentPolicies, isFleetServerPolicy)); - const [selectedAgentPolicy, setSelectedAgentPolicy] = useState(undefined); + const [selectedAgentPolicy, setSelectedAgentPolicy] = useState(policyId); useEffect(() => { setShowCreatePolicy(agentPolicies.length === 0); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx index 539f9f990262..f8ae02fb5a66 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_selection.tsx @@ -154,7 +154,7 @@ export const EnrollmentStepAgentPolicy: React.FC = (props) => { value: agentPolicy.id, text: agentPolicy.name, }))} - value={selectedAgentPolicyId || undefined} + value={selectedAgentPolicyId} onChange={(e) => setSelectedAgentPolicyId(e.target.value)} aria-label={i18n.translate( 'xpack.fleet.enrollmentStepAgentPolicy.policySelectAriaLabel', diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx index b740d0ea62f0..9018f508e93e 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useMemo } from 'react'; import { EuiFlyout, EuiFlyoutBody, @@ -22,7 +22,12 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useGetSettings, sendGetOneAgentPolicy, useFleetStatus } from '../../hooks'; +import { + useGetSettings, + sendGetOneAgentPolicy, + useFleetStatus, + useGetAgentPolicies, +} from '../../hooks'; import { FLEET_SERVER_PACKAGE } from '../../constants'; import type { PackagePolicy } from '../../types'; @@ -47,7 +52,6 @@ export * from './steps'; export const AgentEnrollmentFlyout: React.FunctionComponent = ({ onClose, agentPolicy, - agentPolicies, viewDataStep, defaultMode = 'managed', }) => { @@ -60,6 +64,24 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ const [policyId, setSelectedPolicyId] = useState(agentPolicy?.id); const [isFleetServerPolicySelected, setIsFleetServerPolicySelected] = useState(false); + // loading the latest agentPolicies for add agent flyout + const { + data: agentPoliciesData, + isLoading: isLoadingAgentPolicies, + resendRequest: refreshAgentPolicies, + } = useGetAgentPolicies({ + page: 1, + perPage: 1000, + full: true, + }); + + const agentPolicies = useMemo(() => { + if (!isLoadingAgentPolicies) { + return agentPoliciesData?.items; + } + return []; + }, [isLoadingAgentPolicies, agentPoliciesData?.items]); + useEffect(() => { async function checkPolicyIsFleetServer() { if (policyId && setIsFleetServerPolicySelected) { @@ -143,9 +165,14 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ agentPolicies={agentPolicies} viewDataStep={viewDataStep} isFleetServerPolicySelected={isFleetServerPolicySelected} + refreshAgentPolicies={refreshAgentPolicies} /> ) : ( - + )} diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx index d3294692c9e5..6fac9b889a67 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx @@ -11,13 +11,7 @@ import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/st import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - useGetOneEnrollmentAPIKey, - useLink, - useFleetStatus, - useGetAgents, - useGetAgentPolicies, -} from '../../hooks'; +import { useGetOneEnrollmentAPIKey, useLink, useFleetStatus, useGetAgents } from '../../hooks'; import { ManualInstructions } from '../../components/enrollment_instructions'; import { @@ -34,9 +28,7 @@ import { policyHasFleetServer } from '../../applications/fleet/sections/agents/s import { FLEET_SERVER_PACKAGE } from '../../constants'; import { DownloadStep, AgentPolicySelectionStep, AgentEnrollmentKeySelectionStep } from './steps'; -import type { BaseProps } from './types'; - -type Props = BaseProps; +import type { InstructionProps } from './types'; const DefaultMissingRequirements = () => { const { getHref } = useLink(); @@ -65,7 +57,7 @@ const FleetServerMissingRequirements = () => { return ; }; -export const ManagedInstructions = React.memo( +export const ManagedInstructions = React.memo( ({ agentPolicy, agentPolicies, @@ -73,6 +65,7 @@ export const ManagedInstructions = React.memo( setSelectedPolicyId, isFleetServerPolicySelected, settings, + refreshAgentPolicies, }) => { const fleetStatus = useFleetStatus(); @@ -87,24 +80,15 @@ export const ManagedInstructions = React.memo( showInactive: false, }); - const { data: agentPoliciesData, isLoading: isLoadingAgentPolicies } = useGetAgentPolicies({ - page: 1, - perPage: 1000, - full: true, - }); - const fleetServers = useMemo(() => { - let policies = agentPolicies; - if (!agentPolicies && !isLoadingAgentPolicies) { - policies = agentPoliciesData?.items; - } + const policies = agentPolicies; const fleetServerAgentPolicies: string[] = (policies ?? []) .filter((pol) => policyHasFleetServer(pol)) .map((pol) => pol.id); return (agents?.items ?? []).filter((agent) => fleetServerAgentPolicies.includes(agent.policy_id ?? '') ); - }, [agents, agentPolicies, agentPoliciesData, isLoadingAgentPolicies]); + }, [agents, agentPolicies]); const fleetServerSteps = useMemo(() => { const { @@ -137,6 +121,7 @@ export const ManagedInstructions = React.memo( setSelectedAPIKeyId, setSelectedPolicyId, excludeFleetServer: true, + refreshAgentPolicies, }) : AgentEnrollmentKeySelectionStep({ agentPolicy, selectedApiKeyId, setSelectedAPIKeyId }), DownloadStep(isFleetServerPolicySelected || false), @@ -165,6 +150,7 @@ export const ManagedInstructions = React.memo( setSelectedPolicyId, setSelectedAPIKeyId, agentPolicies, + refreshAgentPolicies, apiKey.data, fleetServerSteps, isFleetServerPolicySelected, diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/standalone_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/standalone_instructions.tsx index 4e5f17509fb2..fa039a73e206 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/standalone_instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/standalone_instructions.tsx @@ -43,233 +43,240 @@ import { import { PlatformSelector } from '../enrollment_instructions/manual/platform_selector'; import { DownloadStep, AgentPolicySelectionStep } from './steps'; -import type { BaseProps } from './types'; +import type { InstructionProps } from './types'; -type Props = BaseProps; +export const StandaloneInstructions = React.memo( + ({ agentPolicy, agentPolicies, refreshAgentPolicies }) => { + const { getHref } = useLink(); + const core = useStartServices(); + const { notifications } = core; -export const StandaloneInstructions = React.memo(({ agentPolicy, agentPolicies }) => { - const { getHref } = useLink(); - const core = useStartServices(); - const { notifications } = core; - - const [selectedPolicyId, setSelectedPolicyId] = useState(agentPolicy?.id); - const [fullAgentPolicy, setFullAgentPolicy] = useState(); - const [isK8s, setIsK8s] = useState<'IS_LOADING' | 'IS_KUBERNETES' | 'IS_NOT_KUBERNETES'>( - 'IS_LOADING' - ); - const [yaml, setYaml] = useState(''); - const linuxMacCommand = - isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_LINUXMAC; - const windowsCommand = - isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_WINDOWS; - const { docLinks } = useStartServices(); - - useEffect(() => { - async function checkifK8s() { - if (!selectedPolicyId) { - return; - } - const agentPolicyRequest = await sendGetOneAgentPolicy(selectedPolicyId); - const agentPol = agentPolicyRequest.data ? agentPolicyRequest.data.item : null; - - if (!agentPol) { - setIsK8s('IS_NOT_KUBERNETES'); - return; - } - const k8s = (pkg: PackagePolicy) => pkg.package?.name === FLEET_KUBERNETES_PACKAGE; - setIsK8s( - (agentPol.package_policies as PackagePolicy[]).some(k8s) - ? 'IS_KUBERNETES' - : 'IS_NOT_KUBERNETES' - ); - } - checkifK8s(); - }, [selectedPolicyId, notifications.toasts]); + const [selectedPolicyId, setSelectedPolicyId] = useState(agentPolicy?.id); + const [fullAgentPolicy, setFullAgentPolicy] = useState(); + const [isK8s, setIsK8s] = useState<'IS_LOADING' | 'IS_KUBERNETES' | 'IS_NOT_KUBERNETES'>( + 'IS_LOADING' + ); + const [yaml, setYaml] = useState(''); + const linuxMacCommand = + isK8s === 'IS_KUBERNETES' + ? KUBERNETES_RUN_INSTRUCTIONS + : STANDALONE_RUN_INSTRUCTIONS_LINUXMAC; + const windowsCommand = + isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_WINDOWS; + const { docLinks } = useStartServices(); - useEffect(() => { - async function fetchFullPolicy() { - try { + useEffect(() => { + async function checkifK8s() { if (!selectedPolicyId) { return; } - let query = { standalone: true, kubernetes: false }; - if (isK8s === 'IS_KUBERNETES') { - query = { standalone: true, kubernetes: true }; - } - const res = await sendGetOneAgentPolicyFull(selectedPolicyId, query); - if (res.error) { - throw res.error; - } + const agentPolicyRequest = await sendGetOneAgentPolicy(selectedPolicyId); + const agentPol = agentPolicyRequest.data ? agentPolicyRequest.data.item : null; - if (!res.data) { - throw new Error('No data while fetching full agent policy'); + if (!agentPol) { + setIsK8s('IS_NOT_KUBERNETES'); + return; } - setFullAgentPolicy(res.data.item); - } catch (error) { - notifications.toasts.addError(error, { - title: 'Error', - }); + const k8s = (pkg: PackagePolicy) => pkg.package?.name === FLEET_KUBERNETES_PACKAGE; + setIsK8s( + (agentPol.package_policies as PackagePolicy[]).some(k8s) + ? 'IS_KUBERNETES' + : 'IS_NOT_KUBERNETES' + ); } - } - if (isK8s !== 'IS_LOADING') { - fetchFullPolicy(); - } - }, [selectedPolicyId, notifications.toasts, isK8s, core.http.basePath]); + checkifK8s(); + }, [selectedPolicyId, notifications.toasts]); - useEffect(() => { - if (isK8s === 'IS_KUBERNETES') { - if (typeof fullAgentPolicy === 'object') { - return; + useEffect(() => { + async function fetchFullPolicy() { + try { + if (!selectedPolicyId) { + return; + } + let query = { standalone: true, kubernetes: false }; + if (isK8s === 'IS_KUBERNETES') { + query = { standalone: true, kubernetes: true }; + } + const res = await sendGetOneAgentPolicyFull(selectedPolicyId, query); + if (res.error) { + throw res.error; + } + + if (!res.data) { + throw new Error('No data while fetching full agent policy'); + } + setFullAgentPolicy(res.data.item); + } catch (error) { + notifications.toasts.addError(error, { + title: 'Error', + }); + } } - setYaml(fullAgentPolicy); - } else { - if (typeof fullAgentPolicy === 'string') { - return; + if (isK8s !== 'IS_LOADING') { + fetchFullPolicy(); } - setYaml(fullAgentPolicyToYaml(fullAgentPolicy, safeDump)); - } - }, [fullAgentPolicy, isK8s]); + }, [selectedPolicyId, notifications.toasts, isK8s, core.http.basePath]); - const policyMsg = - isK8s === 'IS_KUBERNETES' ? ( - ES_USERNAME, - ESPasswordVariable: ES_PASSWORD, - }} - /> - ) : ( - elastic-agent.yml, - ESUsernameVariable: ES_USERNAME, - ESPasswordVariable: ES_PASSWORD, - outputSection: outputs, - }} - /> - ); + useEffect(() => { + if (isK8s === 'IS_KUBERNETES') { + if (typeof fullAgentPolicy === 'object') { + return; + } + setYaml(fullAgentPolicy); + } else { + if (typeof fullAgentPolicy === 'string') { + return; + } + setYaml(fullAgentPolicyToYaml(fullAgentPolicy, safeDump)); + } + }, [fullAgentPolicy, isK8s]); - let downloadLink = ''; - if (selectedPolicyId) { - downloadLink = - isK8s === 'IS_KUBERNETES' - ? core.http.basePath.prepend( - `${agentPolicyRouteService.getInfoFullDownloadPath(selectedPolicyId)}?kubernetes=true` - ) - : core.http.basePath.prepend( - `${agentPolicyRouteService.getInfoFullDownloadPath(selectedPolicyId)}?standalone=true` - ); - } + const policyMsg = + isK8s === 'IS_KUBERNETES' ? ( + ES_USERNAME, + ESPasswordVariable: ES_PASSWORD, + }} + /> + ) : ( + elastic-agent.yml, + ESUsernameVariable: ES_USERNAME, + ESPasswordVariable: ES_PASSWORD, + outputSection: outputs, + }} + /> + ); - const downloadMsg = - isK8s === 'IS_KUBERNETES' ? ( - - ) : ( - - ); + let downloadLink = ''; + if (selectedPolicyId) { + downloadLink = + isK8s === 'IS_KUBERNETES' + ? core.http.basePath.prepend( + `${agentPolicyRouteService.getInfoFullDownloadPath(selectedPolicyId)}?kubernetes=true` + ) + : core.http.basePath.prepend( + `${agentPolicyRouteService.getInfoFullDownloadPath(selectedPolicyId)}?standalone=true` + ); + } - const steps = [ - !agentPolicy - ? AgentPolicySelectionStep({ agentPolicies, setSelectedPolicyId, excludeFleetServer: true }) - : undefined, - DownloadStep(false), - { - title: i18n.translate('xpack.fleet.agentEnrollment.stepConfigureAgentTitle', { - defaultMessage: 'Configure the agent', - }), - children: ( - <> - - <>{policyMsg} - - - - - {(copy) => ( - - - - )} - - - - - <>{downloadMsg} - - - - - - {yaml} - - - - ), - }, - { - title: i18n.translate('xpack.fleet.agentEnrollment.stepRunAgentTitle', { - defaultMessage: 'Start the agent', - }), - children: ( - - ), - }, - { - title: i18n.translate('xpack.fleet.agentEnrollment.stepCheckForDataTitle', { - defaultMessage: 'Check for data', - }), - children: ( - <> - - - - - ), - }} - /> - - - ), - }, - ].filter(Boolean) as EuiContainedStepProps[]; - - return ( - <> - + ) : ( - - - - - ); -}); + ); + + const steps = [ + !agentPolicy + ? AgentPolicySelectionStep({ + agentPolicies, + setSelectedPolicyId, + excludeFleetServer: true, + refreshAgentPolicies, + }) + : undefined, + DownloadStep(false), + { + title: i18n.translate('xpack.fleet.agentEnrollment.stepConfigureAgentTitle', { + defaultMessage: 'Configure the agent', + }), + children: ( + <> + + <>{policyMsg} + + + + + {(copy) => ( + + + + )} + + + + + <>{downloadMsg} + + + + + + {yaml} + + + + ), + }, + { + title: i18n.translate('xpack.fleet.agentEnrollment.stepRunAgentTitle', { + defaultMessage: 'Start the agent', + }), + children: ( + + ), + }, + { + title: i18n.translate('xpack.fleet.agentEnrollment.stepCheckForDataTitle', { + defaultMessage: 'Check for data', + }), + children: ( + <> + + + + + ), + }} + /> + + + ), + }, + ].filter(Boolean) as EuiContainedStepProps[]; + + return ( + <> + + + + + + + ); + } +); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx index 953918a10f15..5e5f26b7317e 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx @@ -81,32 +81,35 @@ export const AgentPolicySelectionStep = ({ selectedApiKeyId, setSelectedAPIKeyId, excludeFleetServer, + refreshAgentPolicies, }: { agentPolicies?: AgentPolicy[]; setSelectedPolicyId?: (policyId?: string) => void; selectedApiKeyId?: string; setSelectedAPIKeyId?: (key?: string) => void; excludeFleetServer?: boolean; + refreshAgentPolicies: () => void; }) => { - const [agentPolicyList, setAgentPolicyList] = useState(agentPolicies || []); - + // storing the created agent policy id as the child component is being recreated + const [policyId, setPolicyId] = useState(undefined); const regularAgentPolicies = useMemo(() => { - return agentPolicyList.filter( + return (agentPolicies ?? []).filter( (policy) => policy && !policy.is_managed && (!excludeFleetServer || !policyHasFleetServer(policy)) ); - }, [agentPolicyList, excludeFleetServer]); + }, [agentPolicies, excludeFleetServer]); const onAgentPolicyChange = useCallback( async (key?: string, policy?: AgentPolicy) => { if (policy) { - setAgentPolicyList([...agentPolicyList, policy]); + refreshAgentPolicies(); } if (setSelectedPolicyId) { setSelectedPolicyId(key); + setPolicyId(key); } }, - [setSelectedPolicyId, setAgentPolicyList, agentPolicyList] + [setSelectedPolicyId, refreshAgentPolicies] ); return { @@ -122,6 +125,7 @@ export const AgentPolicySelectionStep = ({ onKeyChange={setSelectedAPIKeyId} onAgentPolicyChange={onAgentPolicyChange} excludeFleetServer={excludeFleetServer} + policyId={policyId} /> ), diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts index 282a5b243cae..e5a3d345dba3 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts @@ -34,3 +34,7 @@ export interface BaseProps { isFleetServerPolicySelected?: boolean; } + +export interface InstructionProps extends BaseProps { + refreshAgentPolicies: () => void; +}