From 076d6d8884d8acdee4bfbcee8e5cccd7b6a89b78 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Tue, 11 Apr 2023 18:58:37 -0300 Subject: [PATCH 01/31] WIP: Cloud Formation --- .../cloud_formation_instructions.tsx | 121 ++++++++++++++++++ .../public/components/platform_selector.tsx | 26 +++- .../fleet/public/hooks/use_platform.tsx | 17 ++- 3 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx new file mode 100644 index 0000000000000..6854057af3162 --- /dev/null +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx @@ -0,0 +1,121 @@ +/* + * 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, { useEffect, useState } from 'react'; +import { + EuiText, + EuiButton, + EuiSpacer, + EuiFlexGroup, + EuiFlexItem, + EuiCopy, + EuiCodeBlock, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; + +import { useGetSettings, useStartServices } from '../../hooks'; + +import { agentPolicyRouteService } from '../../../common'; + +import { sendGetK8sManifest } from '../../hooks/use_request/k8s'; + +interface Props { + enrollmentAPIKey?: string; + onCopy?: () => void; + onDownload?: () => void; +} + +export const CloudFormationInstructions: React.FunctionComponent = ({ + enrollmentAPIKey, + onCopy, + onDownload, +}) => { + const core = useStartServices(); + const settings = useGetSettings(); + const { notifications } = core; + + const [yaml, setYaml] = useState(''); + const [fleetServer, setFleetServer] = useState(); + const [copyButtonClicked, setCopyButtonClicked] = useState(false); + const [downloadButtonClicked, setDownloadButtonClicked] = useState(false); + + const onCopyButtonClick = (copy: () => void) => { + copy(); + setCopyButtonClicked(true); + if (onCopy) onCopy(); + }; + + const onDownloadButtonClick = (downloadLink: string) => { + setDownloadButtonClicked(true); + if (onDownload) onDownload(); + window.location.href = downloadLink; + }; + + useEffect(() => { + async function fetchK8sManifest() { + try { + const fleetServerHosts = settings.data?.item.fleet_server_hosts; + let host = ''; + if (fleetServerHosts !== undefined && fleetServerHosts.length !== 0) { + setFleetServer(fleetServerHosts[0]); + host = fleetServerHosts[0]; + } + const query = { fleetServer: host, enrolToken: enrollmentAPIKey }; + const res = await sendGetK8sManifest(query); + if (res.error) { + throw res.error; + } + + if (!res.data) { + throw new Error('No data while fetching agent manifest'); + } + + setYaml(res.data.item); + } catch (error) { + notifications.toasts.addError(error, { + title: i18n.translate('xpack.fleet.agentEnrollment.loadk8sManifestErrorTitle', { + defaultMessage: 'Error while fetching agent manifest', + }), + }); + } + } + fetchK8sManifest(); + }, [notifications.toasts, enrollmentAPIKey, settings.data?.item.fleet_server_hosts]); + + const downloadDescription = ( + + ); + + const downloadLink = core.http.basePath.prepend( + `${agentPolicyRouteService.getK8sFullDownloadPath()}?fleetServer=${fleetServer}&enrolToken=${enrollmentAPIKey}` + ); + + const k8sYaml = ( + + {yaml} + + ); + + return ( + <> + {downloadDescription} + + onDownloadButtonClick(downloadLink)} + > + Run CloudFormation + + + ); +}; diff --git a/x-pack/plugins/fleet/public/components/platform_selector.tsx b/x-pack/plugins/fleet/public/components/platform_selector.tsx index d0058185171ab..c653e28df5ea6 100644 --- a/x-pack/plugins/fleet/public/components/platform_selector.tsx +++ b/x-pack/plugins/fleet/public/components/platform_selector.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import styled from 'styled-components'; import { EuiText, @@ -19,10 +19,11 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { PLATFORM_TYPE } from '../hooks'; +import { CLOUD_FORMATION_PLATFORM_OPTION, type PLATFORM_TYPE } from '../hooks'; import { REDUCED_PLATFORM_OPTIONS, PLATFORM_OPTIONS, usePlatform } from '../hooks'; import { KubernetesInstructions } from './agent_enrollment_flyout/kubernetes_instructions'; +import { CloudFormationInstructions } from './agent_enrollment_flyout/cloud_formation_instructions'; interface Props { linuxCommand: string; @@ -52,6 +53,7 @@ export const PlatformSelector: React.FunctionComponent = ({ linuxDebCommand, linuxRpmCommand, k8sCommand, + hasCloudFormationIntegration = true, hasK8sIntegration, hasK8sIntegrationMultiPage, isManaged, @@ -68,7 +70,17 @@ export const PlatformSelector: React.FunctionComponent = ({ // In case of fleet server installation or standalone agent without // Kubernetes integration in the policy use reduced platform options - const useReduce = hasFleetServer || (!isManaged && !hasK8sIntegration); + const isReduced = hasFleetServer || (!isManaged && !hasK8sIntegration); + + const getPlatformOptions = useCallback(() => { + const platformOptions = isReduced ? REDUCED_PLATFORM_OPTIONS : PLATFORM_OPTIONS; + + if (hasCloudFormationIntegration) { + return platformOptions.concat(CLOUD_FORMATION_PLATFORM_OPTION); + } + + return platformOptions; + }, [hasCloudFormationIntegration, isReduced]); const [copyButtonClicked, setCopyButtonClicked] = useState(false); @@ -116,7 +128,7 @@ export const PlatformSelector: React.FunctionComponent = ({ <> {!hasK8sIntegrationMultiPage && ( setPlatform(id as PLATFORM_TYPE)} legend={i18n.translate('xpack.fleet.enrollmentInstructions.platformSelectAriaLabel', { @@ -147,6 +159,12 @@ export const PlatformSelector: React.FunctionComponent = ({ )} + {platform === 'cloudFormation' && ( + <> + + + + )} {!hasK8sIntegrationMultiPage && ( <> {platform === 'kubernetes' && ( diff --git a/x-pack/plugins/fleet/public/hooks/use_platform.tsx b/x-pack/plugins/fleet/public/hooks/use_platform.tsx index edc5dba9eebbe..2d5e0d8ee7492 100644 --- a/x-pack/plugins/fleet/public/hooks/use_platform.tsx +++ b/x-pack/plugins/fleet/public/hooks/use_platform.tsx @@ -8,7 +8,14 @@ import { useState } from 'react'; import { i18n } from '@kbn/i18n'; -export type PLATFORM_TYPE = 'linux' | 'mac' | 'windows' | 'rpm' | 'deb' | 'kubernetes'; +export type PLATFORM_TYPE = + | 'linux' + | 'mac' + | 'windows' + | 'rpm' + | 'deb' + | 'kubernetes' + | 'cloudFormation'; export const REDUCED_PLATFORM_OPTIONS: Array<{ label: string; @@ -52,6 +59,14 @@ export const REDUCED_PLATFORM_OPTIONS: Array<{ }, ]; +export const CLOUD_FORMATION_PLATFORM_OPTION = { + id: 'cloudFormation', + label: i18n.translate('xpack.fleet.enrollmentInstructions.platformButtons.cloudFormation', { + defaultMessage: 'Cloud Formation', + }), + 'data-test-subj': 'platformTypeCloudFormation', +}; + export const PLATFORM_OPTIONS = [ ...REDUCED_PLATFORM_OPTIONS, { From d1aabeca026c7ae66edd15ac719a45f2deca05f8 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Wed, 12 Apr 2023 15:23:19 -0300 Subject: [PATCH 02/31] wip: cloud formation instructions --- .../cloud_formation_instructions.tsx | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx index 6854057af3162..15b3bd0cbed76 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx @@ -87,34 +87,24 @@ export const CloudFormationInstructions: React.FunctionComponent = ({ fetchK8sManifest(); }, [notifications.toasts, enrollmentAPIKey, settings.data?.item.fleet_server_hosts]); - const downloadDescription = ( - - ); - const downloadLink = core.http.basePath.prepend( `${agentPolicyRouteService.getK8sFullDownloadPath()}?fleetServer=${fleetServer}&enrolToken=${enrollmentAPIKey}` ); - const k8sYaml = ( - - {yaml} - - ); - return ( <> - {downloadDescription} onDownloadButtonClick(downloadLink)} > - Run CloudFormation + ); From e82be936b582e07367638b967dfa41871c7f070e Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 08:26:37 -0300 Subject: [PATCH 03/31] adding vuln mgmt mocks --- .../components/fleet_extensions/mocks.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts index 219b551c14260..2a32aa8dfd433 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts @@ -5,6 +5,7 @@ * 2.0. */ import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; +import type { PackageInfo } from '@kbn/fleet-plugin/common'; import { createNewPackagePolicyMock } from '@kbn/fleet-plugin/common/mocks'; import { CLOUDBEAT_GCP, @@ -12,12 +13,48 @@ import { CLOUDBEAT_EKS, CLOUDBEAT_VANILLA, CLOUDBEAT_AWS, + CLOUDBEAT_VULN_MGMT_AWS, } from '../../../common/constants'; import type { PostureInput } from '../../../common/types'; export const getMockPolicyAWS = () => getPolicyMock(CLOUDBEAT_AWS, 'cspm', 'aws'); export const getMockPolicyK8s = () => getPolicyMock(CLOUDBEAT_VANILLA, 'kspm', 'self_managed'); export const getMockPolicyEKS = () => getPolicyMock(CLOUDBEAT_EKS, 'kspm', 'eks'); +export const getMockPolicyVulnMgmtAWS = () => + getPolicyMock(CLOUDBEAT_VULN_MGMT_AWS, 'vuln_mgmt', 'aws'); + +export const getMockPackageInfoVulnMgmtAWS = () => { + return { + policy_templates: [ + { + title: '', + description: '', + name: 'vuln_mgmt', + inputs: [ + { + type: 'cloudbeat/vuln_mgmt_aws', + title: '', + description: '', + vars: [ + { + type: 'text', + name: 'cloud_formation_template', + default: 's3_url', + show_user: false, + }, + { + type: 'text', + name: 'cloud_formation_stack_name', + default: 'stack_name', + show_user: false, + }, + ], + }, + ], + }, + ], + } as PackageInfo; +}; const getPolicyMock = ( type: PostureInput, @@ -84,6 +121,12 @@ const getPolicyMock = ( enabled: false, streams: [{ enabled: false, data_stream: dataStream }], }, + { + type: CLOUDBEAT_VULN_MGMT_AWS, + policy_template: 'vuln_mgmt', + enabled: type === CLOUDBEAT_VULN_MGMT_AWS, + streams: [{ enabled: false, data_stream: dataStream }], + }, ], }; }; From fc2c9c4d0f4d9229611247d6c98c76017529f738 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 08:49:11 -0300 Subject: [PATCH 04/31] add vuln mgmt cloud formation utils --- .../components/fleet_extensions/utils.ts | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts index 8e73426281039..e3e67b0e4dd78 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts @@ -7,7 +7,10 @@ import type { NewPackagePolicy, NewPackagePolicyInput, + PackageInfo, PackagePolicyConfigRecordEntry, + RegistryPolicyTemplate, + RegistryVarsEntry, } from '@kbn/fleet-plugin/common'; import merge from 'lodash/merge'; import { @@ -21,6 +24,7 @@ import { SUPPORTED_CLOUDBEAT_INPUTS, CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE, + VULN_MGMT_POLICY_TEMPLATE, } from '../../../common/constants'; import { DEFAULT_AWS_VARS_GROUP } from './aws_credentials_form'; import type { PostureInput, CloudSecurityPolicyTemplate } from '../../../common/types'; @@ -34,7 +38,8 @@ type PosturePolicyInput = | { type: typeof CLOUDBEAT_GCP; policy_template: typeof CSPM_POLICY_TEMPLATE } | { type: typeof CLOUDBEAT_AWS; policy_template: typeof CSPM_POLICY_TEMPLATE } | { type: typeof CLOUDBEAT_VANILLA; policy_template: typeof KSPM_POLICY_TEMPLATE } - | { type: typeof CLOUDBEAT_EKS; policy_template: typeof KSPM_POLICY_TEMPLATE }; + | { type: typeof CLOUDBEAT_EKS; policy_template: typeof KSPM_POLICY_TEMPLATE } + | { type: typeof CLOUDBEAT_VULN_MGMT_AWS; policy_template: typeof VULN_MGMT_POLICY_TEMPLATE }; // Extend NewPackagePolicyInput with known string literals for input type and policy template export type NewPackagePolicyPostureInput = NewPackagePolicyInput & PosturePolicyInput; @@ -121,6 +126,52 @@ export const getPosturePolicy = ( }), }); +type RegistryPolicyTemplateWithInputs = RegistryPolicyTemplate & { + inputs: Array<{ + vars?: RegistryVarsEntry[]; + }>; +}; +// type guard for checking inputs +export const hasPolicyTemplateInputs = ( + policyTemplate: RegistryPolicyTemplate +): policyTemplate is RegistryPolicyTemplateWithInputs => { + return policyTemplate.hasOwnProperty('inputs'); +}; + +interface CloudFormation { + templateUrl: string; + stackName: string; +} + +const emptyCloudFormation: CloudFormation = { + templateUrl: '', + stackName: '', +}; + +export const getVulnMgmtCloudFormation = (packageInfo: PackageInfo): CloudFormation => { + if (!packageInfo.policy_templates) return emptyCloudFormation; + + const vulnMgmtPolicyTemplate = packageInfo.policy_templates.find((p) => p.name === 'vuln_mgmt'); + if (!vulnMgmtPolicyTemplate) return emptyCloudFormation; + + const vulnMgmtInputs = + hasPolicyTemplateInputs(vulnMgmtPolicyTemplate) && vulnMgmtPolicyTemplate.inputs; + + if (!vulnMgmtInputs) return emptyCloudFormation; + if (!vulnMgmtInputs[0].vars) return emptyCloudFormation; + + const cloudFormationTemplate = vulnMgmtInputs[0].vars.find( + (v) => v.name === 'cloud_formation_template' + ); + const cloudFormationStackName = vulnMgmtInputs[0].vars.find( + (v) => v.name === 'cloud_formation_stack_name' + ); + return { + templateUrl: String(cloudFormationTemplate?.default || ''), + stackName: String(cloudFormationStackName?.default || 'Elastic-Vulnerability-Management'), + }; +}; + /** * Input vars that are hidden from the user */ From 30825a8c1768f5c0c3770bdc961fee3159292443 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 08:49:51 -0300 Subject: [PATCH 05/31] add use cloud formation hook to policy template form --- .../fleet_extensions/policy_template_form.tsx | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx index 0c5e237bfccb1..afbbc89f27025 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx @@ -20,6 +20,7 @@ import type { NewPackagePolicyInput, PackagePolicyReplaceDefineStepExtensionComponentProps, } from '@kbn/fleet-plugin/public/types'; +import type { AgentPolicy, PackageInfo } from '@kbn/fleet-plugin/common'; import { useParams } from 'react-router-dom'; import type { PostureInput, CloudSecurityPolicyTemplate } from '../../../common/types'; import { @@ -28,10 +29,12 @@ import { CLOUDBEAT_VULN_MGMT_AWS, CSPM_POLICY_TEMPLATE, SUPPORTED_POLICY_TEMPLATES, + VULN_MGMT_POLICY_TEMPLATE, } from '../../../common/constants'; import { getPosturePolicy, getPostureInputHiddenVars, + getVulnMgmtCloudFormation, POSTURE_NAMESPACE, type NewPackagePolicyPostureInput, isPostureInput, @@ -85,11 +88,20 @@ const IntegrationSettings = ({ onChange, fields }: IntegrationInfoFieldsProps) = ); export const CspPolicyTemplateForm = memo( - ({ newPolicy, onChange, validationResults, isEditPage }) => { + ({ + newPolicy, + onChange, + validationResults, + isEditPage, + packageInfo, + agentPolicy, + onChangeAgentPolicy, + }) => { const integrationParam = useParams<{ integration: CloudSecurityPolicyTemplate }>().integration; const integration = SUPPORTED_POLICY_TEMPLATES.includes(integrationParam) ? integrationParam : undefined; + const input = getSelectedOption(newPolicy.inputs, integration); const updatePolicy = useCallback( @@ -144,6 +156,8 @@ export const CspPolicyTemplateForm = memo @@ -291,3 +305,43 @@ const getSelectedOption = ( return selectedOption; }; + +/** + * Update CloudFormation template and stack name in the Agent Policy + * based on the selected policy template + */ +const useCloudFormationTemplate = ({ + input, + agentPolicy = {} as AgentPolicy, + onChangeAgentPolicy, + packageInfo, +}: { + input: NewPackagePolicyPostureInput; + agentPolicy?: AgentPolicy; + onChangeAgentPolicy: (agentPolicy: AgentPolicy) => void; + packageInfo: PackageInfo; +}) => { + useEffect(() => { + // CloudFormation currently only supported by Vuln Mgmt + if (input.policy_template !== VULN_MGMT_POLICY_TEMPLATE) { + // Clear CloudFormation template URL and stack name + // when switching to a non-Vuln Mgmt policy template + onChangeAgentPolicy({ + ...agentPolicy, + cloud_formation_template_url: '', + cloud_formation_stack_name: '', + }); + return; + } + const { stackName, templateUrl } = getVulnMgmtCloudFormation(packageInfo); + + if (templateUrl === '') return; + if (agentPolicy?.cloud_formation_template_url === templateUrl) return; + onChangeAgentPolicy({ + ...agentPolicy, + cloud_formation_template_url: templateUrl, + cloud_formation_stack_name: stackName, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [agentPolicy?.cloud_formation_template_url, input.policy_template]); +}; From 735c0263b12e57bb4e2eab8c970a9d747752b39e Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 08:50:14 -0300 Subject: [PATCH 06/31] add test for vuln mgmt --- .../policy_template_form.test.tsx | 63 ++++++++++++++++--- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx index 4fccd79b8238d..9e44d34bfe99b 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx @@ -8,8 +8,19 @@ import React from 'react'; import { render } from '@testing-library/react'; import { CspPolicyTemplateForm } from './policy_template_form'; import { TestProvider } from '../../test/test_provider'; -import { getMockPolicyAWS, getMockPolicyEKS, getMockPolicyK8s } from './mocks'; -import type { NewPackagePolicy, PackageInfo, PackagePolicy } from '@kbn/fleet-plugin/common'; +import { + getMockPackageInfoVulnMgmtAWS, + getMockPolicyAWS, + getMockPolicyEKS, + getMockPolicyK8s, + getMockPolicyVulnMgmtAWS, +} from './mocks'; +import type { + AgentPolicy, + NewPackagePolicy, + PackageInfo, + PackagePolicy, +} from '@kbn/fleet-plugin/common'; import userEvent from '@testing-library/user-event'; import { getPosturePolicy } from './utils'; import { CLOUDBEAT_AWS, CLOUDBEAT_EKS } from '../../../common/constants'; @@ -23,21 +34,28 @@ jest.mock('react-router-dom', () => ({ }), })); +const onChange = jest.fn(); +const onChangeAgentPolicy = jest.fn(); + describe('', () => { beforeEach(() => { (useParams as jest.Mock).mockReturnValue({ integration: undefined, }); + onChange.mockClear(); + onChangeAgentPolicy.mockClear(); }); - const onChange = jest.fn(); - const WrappedComponent = ({ newPolicy, edit = false, + agentPolicy, + packageInfo = {} as PackageInfo, }: { edit?: boolean; newPolicy: NewPackagePolicy; + agentPolicy?: AgentPolicy; + packageInfo?: PackageInfo; }) => ( {edit && ( @@ -45,25 +63,25 @@ describe('', () => { policy={newPolicy as PackagePolicy} newPolicy={newPolicy} onChange={onChange} - packageInfo={{} as PackageInfo} + packageInfo={packageInfo} isEditPage={true} + onChangeAgentPolicy={onChangeAgentPolicy} + agentPolicy={agentPolicy} /> )} {!edit && ( )} ); - beforeEach(() => { - onChange.mockClear(); - }); - it('updates package policy namespace to default when it changes', () => { const policy = getMockPolicyK8s(); const { rerender } = render(); @@ -140,6 +158,12 @@ describe('', () => { isValid: true, updatedPolicy: eksPolicy, }); + + // make sure CloudFormation template is reset + expect(onChangeAgentPolicy).toHaveBeenCalledWith({ + cloud_formation_template_url: '', + cloud_formation_stack_name: '', + }); }); it('renders CSPM input selector', () => { @@ -156,6 +180,12 @@ describe('', () => { expect(option2).toBeDisabled(); expect(option3).toBeDisabled(); expect(option1).toBeChecked(); + + // make sure CloudFormation template is reset + expect(onChangeAgentPolicy).toHaveBeenCalledWith({ + cloud_formation_template_url: '', + cloud_formation_stack_name: '', + }); }); it('renders disabled KSPM input when editing', () => { @@ -438,4 +468,17 @@ describe('', () => { }); }); } + + describe('Vuln Mgmt', () => { + it('Update Agent Policy CloudFormation template and stack name from vars', () => { + const policy = getMockPolicyVulnMgmtAWS(); + const packageInfo = getMockPackageInfoVulnMgmtAWS(); + render(); + + expect(onChangeAgentPolicy).toHaveBeenNthCalledWith(1, { + cloud_formation_template_url: 's3_url', + cloud_formation_stack_name: 'stack_name', + }); + }); + }); }); From 22be93fbcea2e0aaa340dc7d703a4c73984456ea Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 08:50:39 -0300 Subject: [PATCH 07/31] add cloud formation types --- x-pack/plugins/fleet/common/types/models/agent_policy.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index 2f0f5adae5978..a901d1f3fb290 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -33,6 +33,8 @@ export interface NewAgentPolicy { fleet_server_host_id?: string | null; schema_version?: string; agent_features?: Array<{ name: string; enabled: boolean }>; + cloud_formation_stack_name?: string; + cloud_formation_template_url?: string; } export interface AgentPolicy extends Omit { From 4369ef6273ce574c06520d901c89a30133648ff3 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 08:54:54 -0300 Subject: [PATCH 08/31] Add CloudFormation in agent policy --- .../utils/install_command_utils.ts | 4 + .../agent_policy_advanced_fields/index.tsx | 96 +++++++++++++++++++ .../agent_policy_validation.test.tsx | 7 ++ .../components/agent_policy_validation.tsx | 12 +++ 4 files changed, 119 insertions(+) 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 index c5a58bef884f1..32abbe6479ecf 100644 --- 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 @@ -52,6 +52,9 @@ function getArtifact(platform: PLATFORM_TYPE, kibanaVersion: string) { kubernetes: { downloadCommand: '', }, + cloudFormation: { + downloadCommand: '', + }, }; return artifactMap[platform]; @@ -113,6 +116,7 @@ export function getInstallCommandForPlatform( deb: `${artifact.downloadCommand}\nsudo elastic-agent enroll ${commandArgumentsStr}\nsudo systemctl enable elastic-agent\nsudo systemctl start elastic-agent`, rpm: `${artifact.downloadCommand}\nsudo elastic-agent enroll ${commandArgumentsStr}\nsudo systemctl enable elastic-agent\nsudo systemctl start elastic-agent`, kubernetes: '', + cloudFormation: '', }; return commands[platform]; 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 407352a69bebf..bfa8e06a12a0e 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 @@ -642,6 +642,101 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent = /> + + +   + + + } + description={ + + } + > + + } + helpText={ + + } + > + updateAgentPolicy({ cloud_formation_template_url: e.target.value })} + isInvalid={Boolean( + touchedFields.cloud_formation_template_url && validation.cloud_formation_template_url + )} + onBlur={() => + setTouchedFields({ ...touchedFields, cloud_formation_template_url: true }) + } + placeholder={i18n.translate( + 'xpack.fleet.agentPolicyForm.cloudFormationTemplateFieldPlaceholder', + { + defaultMessage: 'CloudFormation template URL', + } + )} + /> + + + } + > + updateAgentPolicy({ cloud_formation_stack_name: e.target.value })} + isInvalid={Boolean( + touchedFields.cloud_formation_stack_name && validation.cloud_formation_stack_name + )} + onBlur={() => setTouchedFields({ ...touchedFields, cloud_formation_stack_name: true })} + placeholder={i18n.translate( + 'xpack.fleet.agentPolicyForm.cloudFormationStackNameFieldPlaceholder', + { + defaultMessage: 'Optional CloudFormation Stack Name', + } + )} + /> + + {isEditing && 'id' in agentPolicy && !agentPolicy.is_managed ? ( = } /> ) : null} + ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.test.tsx index b6cf950b1320e..0d07ca47216bf 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.test.tsx @@ -47,4 +47,11 @@ describe('Agent Policy form validation', () => { }); expect(result.inactivity_timeout).toBeDefined(); }); + it('should return error when CloudFormation template is set with empty Stack Name', () => { + const result = agentPolicyFormValidation({ + cloud_formation_template_url: 'some-url', + cloud_formation_stack_name: '', + }); + expect(result.cloud_formation_stack_name).toBeDefined(); + }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx index 4ba769c311523..13a1162b29b49 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx @@ -51,5 +51,17 @@ export const agentPolicyFormValidation = ( ]; } + if ( + agentPolicy.cloud_formation_template_url !== '' && + agentPolicy.cloud_formation_stack_name === '' + ) { + errors.cloud_formation_stack_name = [ + , + ]; + } + return errors; }; From cddaea81ed5d1cd3f7747f5c955c522caddeb631 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 08:56:27 -0300 Subject: [PATCH 09/31] add CloudFormation to agent management --- .../install_agent/install_agent_managed.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx index 8b4ff121f3e2a..991efdd37716e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx @@ -19,6 +19,8 @@ import { } from '../../../../../../../../../components/agent_enrollment_flyout/steps'; import { ManualInstructions } from '../../../../../../../../../components/enrollment_instructions'; +import type { CloudFormation } from '../../../../../../../../../components/agent_enrollment_flyout/types'; + import { KubernetesManifestApplyStep } from '../../../../../../../../../components/agent_enrollment_flyout/steps/run_k8s_apply_command_step'; import type { InstallAgentPageProps } from './types'; @@ -62,6 +64,14 @@ export const InstallElasticAgentManagedPageStep: React.FC const isK8s = props.packageInfo.name === 'kubernetes' ? 'IS_KUBERNETES_MULTIPAGE' : 'IS_NOT_KUBERNETES'; + const cloudFormation: CloudFormation | undefined = + agentPolicy?.cloud_formation_template_url && agentPolicy?.cloud_formation_stack_name + ? { + templateUrl: agentPolicy.cloud_formation_template_url, + stackName: agentPolicy.cloud_formation_stack_name, + } + : undefined; + const installManagedCommands = ManualInstructions({ apiKey: enrollmentAPIKey.api_key, fleetProxy, @@ -79,6 +89,7 @@ export const InstallElasticAgentManagedPageStep: React.FC isComplete: commandCopied || !!enrolledAgentIds.length, fullCopyButton: true, onCopy: () => setCommandCopied(true), + cloudFormation, }), ]; From 735f50443ae5884275412f0043dcb91490ccbad3 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 09:00:13 -0300 Subject: [PATCH 10/31] add method to update AgentPolicy on integration --- .../single_page_layout/index.tsx | 1 + .../components/settings/index.tsx | 2 ++ .../public/components/platform_selector.tsx | 19 +++++++++++++------ .../fleet/public/hooks/use_platform.tsx | 2 +- .../fleet/public/types/ui_extensions.ts | 1 + 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx index a13d0ea31a33d..54738b5d06ba3 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx @@ -291,6 +291,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ onChange={handleExtensionViewOnChange} validationResults={validationResults} isEditPage={false} + onChangeAgentPolicy={updateNewAgentPolicy} /> ) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx index 6080f8cd80710..1bcdc8fcd0b90 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx @@ -53,6 +53,8 @@ const pickAgentPolicyKeysToSend = (agentPolicy: AgentPolicy) => 'download_source_id', 'fleet_server_host_id', 'agent_features', + 'cloud_formation_template_url', + 'cloud_formation_stack_name', ]); const FormWrapper = styled.div` diff --git a/x-pack/plugins/fleet/public/components/platform_selector.tsx b/x-pack/plugins/fleet/public/components/platform_selector.tsx index c653e28df5ea6..f3ba1ae23a548 100644 --- a/x-pack/plugins/fleet/public/components/platform_selector.tsx +++ b/x-pack/plugins/fleet/public/components/platform_selector.tsx @@ -25,6 +25,8 @@ import { REDUCED_PLATFORM_OPTIONS, PLATFORM_OPTIONS, usePlatform } from '../hook import { KubernetesInstructions } from './agent_enrollment_flyout/kubernetes_instructions'; import { CloudFormationInstructions } from './agent_enrollment_flyout/cloud_formation_instructions'; +import type { CloudFormation } from './agent_enrollment_flyout/types'; + interface Props { linuxCommand: string; macCommand: string; @@ -39,6 +41,7 @@ interface Props { enrollToken?: string | undefined; fullCopyButton?: boolean; onCopy?: () => void; + cloudFormation?: CloudFormation; } // Otherwise the copy button is over the text @@ -53,7 +56,6 @@ export const PlatformSelector: React.FunctionComponent = ({ linuxDebCommand, linuxRpmCommand, k8sCommand, - hasCloudFormationIntegration = true, hasK8sIntegration, hasK8sIntegrationMultiPage, isManaged, @@ -61,6 +63,7 @@ export const PlatformSelector: React.FunctionComponent = ({ hasFleetServer, fullCopyButton, onCopy, + cloudFormation, }) => { const { platform, setPlatform } = usePlatform(); @@ -75,12 +78,12 @@ export const PlatformSelector: React.FunctionComponent = ({ const getPlatformOptions = useCallback(() => { const platformOptions = isReduced ? REDUCED_PLATFORM_OPTIONS : PLATFORM_OPTIONS; - if (hasCloudFormationIntegration) { + if (cloudFormation) { return platformOptions.concat(CLOUD_FORMATION_PLATFORM_OPTION); } return platformOptions; - }, [hasCloudFormationIntegration, isReduced]); + }, [cloudFormation, isReduced]); const [copyButtonClicked, setCopyButtonClicked] = useState(false); @@ -113,6 +116,7 @@ export const PlatformSelector: React.FunctionComponent = ({ deb: linuxDebCommand, rpm: linuxRpmCommand, kubernetes: k8sCommand, + cloudFormation: '', }; const onTextAreaClick = () => { if (onCopy) onCopy(); @@ -159,13 +163,16 @@ export const PlatformSelector: React.FunctionComponent = ({ )} - {platform === 'cloudFormation' && ( + {platform === 'cloudFormation' && cloudFormation && ( <> - + )} - {!hasK8sIntegrationMultiPage && ( + {!hasK8sIntegrationMultiPage && !cloudFormation && ( <> {platform === 'kubernetes' && ( diff --git a/x-pack/plugins/fleet/public/hooks/use_platform.tsx b/x-pack/plugins/fleet/public/hooks/use_platform.tsx index 2d5e0d8ee7492..e127440937737 100644 --- a/x-pack/plugins/fleet/public/hooks/use_platform.tsx +++ b/x-pack/plugins/fleet/public/hooks/use_platform.tsx @@ -62,7 +62,7 @@ export const REDUCED_PLATFORM_OPTIONS: Array<{ export const CLOUD_FORMATION_PLATFORM_OPTION = { id: 'cloudFormation', label: i18n.translate('xpack.fleet.enrollmentInstructions.platformButtons.cloudFormation', { - defaultMessage: 'Cloud Formation', + defaultMessage: 'CloudFormation', }), 'data-test-subj': 'platformTypeCloudFormation', }; diff --git a/x-pack/plugins/fleet/public/types/ui_extensions.ts b/x-pack/plugins/fleet/public/types/ui_extensions.ts index 53ae5322f0d9d..f2c1e2350359b 100644 --- a/x-pack/plugins/fleet/public/types/ui_extensions.ts +++ b/x-pack/plugins/fleet/public/types/ui_extensions.ts @@ -36,6 +36,7 @@ export type PackagePolicyReplaceDefineStepExtensionComponentProps = ( validationResults?: PackagePolicyValidationResults; agentPolicy?: AgentPolicy; packageInfo: PackageInfo; + onChangeAgentPolicy: (newAgentPolicy: Partial) => void; }; /** From 78bf72de84e5d348dd80877c5e7198ef89d2e4dc Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 09:00:40 -0300 Subject: [PATCH 11/31] update AgentPolicy saved object --- x-pack/plugins/fleet/server/saved_objects/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 8f0db94d1d31c..dc5f2ae33905c 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -121,6 +121,8 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ enabled: { type: 'boolean' }, }, }, + cloud_formation_template_url: { type: 'text' }, + cloud_formation_stack_name: { type: 'text' }, }, }, migrations: { From 605ef26790f2d85a1d90e593ee9e24649de46c19 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 09:00:56 -0300 Subject: [PATCH 12/31] update AgentPolicy Schema --- x-pack/plugins/fleet/server/types/models/agent_policy.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/x-pack/plugins/fleet/server/types/models/agent_policy.ts b/x-pack/plugins/fleet/server/types/models/agent_policy.ts index f85bd93b1e870..0a413a2cffe67 100644 --- a/x-pack/plugins/fleet/server/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/agent_policy.ts @@ -47,6 +47,14 @@ export const AgentPolicyBaseSchema = { }) ) ), + cloud_formation_template_url: schema.maybe(schema.nullable(schema.string())), + // cloud_formation_stack_name: schema.maybe(schema.nullable(schema.string())), + cloud_formation_stack_name: schema.conditional( + schema.siblingRef('cloud_formation_template_url'), + schema.string({ minLength: 1 }), + schema.string({ minLength: 1 }), + schema.maybe(schema.nullable(schema.string())) + ), }; export const NewAgentPolicySchema = schema.object({ From 37f18f947059f52e5cbfef422001681cd0a6e34e Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 09:40:25 -0300 Subject: [PATCH 13/31] Refactor: allow cloud formation to be more generic --- .../components/fleet_extensions/mocks.ts | 6 ---- .../policy_template_form.test.tsx | 3 -- .../fleet_extensions/policy_template_form.tsx | 4 +-- .../components/fleet_extensions/utils.ts | 29 +++++-------------- 4 files changed, 8 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts index 2a32aa8dfd433..0002fa01fa528 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts @@ -42,12 +42,6 @@ export const getMockPackageInfoVulnMgmtAWS = () => { default: 's3_url', show_user: false, }, - { - type: 'text', - name: 'cloud_formation_stack_name', - default: 'stack_name', - show_user: false, - }, ], }, ], diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx index 9e44d34bfe99b..5abd218e72ddf 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx @@ -162,7 +162,6 @@ describe('', () => { // make sure CloudFormation template is reset expect(onChangeAgentPolicy).toHaveBeenCalledWith({ cloud_formation_template_url: '', - cloud_formation_stack_name: '', }); }); @@ -184,7 +183,6 @@ describe('', () => { // make sure CloudFormation template is reset expect(onChangeAgentPolicy).toHaveBeenCalledWith({ cloud_formation_template_url: '', - cloud_formation_stack_name: '', }); }); @@ -477,7 +475,6 @@ describe('', () => { expect(onChangeAgentPolicy).toHaveBeenNthCalledWith(1, { cloud_formation_template_url: 's3_url', - cloud_formation_stack_name: 'stack_name', }); }); }); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx index afbbc89f27025..4fab1f1016150 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx @@ -329,18 +329,16 @@ const useCloudFormationTemplate = ({ onChangeAgentPolicy({ ...agentPolicy, cloud_formation_template_url: '', - cloud_formation_stack_name: '', }); return; } - const { stackName, templateUrl } = getVulnMgmtCloudFormation(packageInfo); + const templateUrl = getVulnMgmtCloudFormation(packageInfo); if (templateUrl === '') return; if (agentPolicy?.cloud_formation_template_url === templateUrl) return; onChangeAgentPolicy({ ...agentPolicy, cloud_formation_template_url: templateUrl, - cloud_formation_stack_name: stackName, }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [agentPolicy?.cloud_formation_template_url, input.policy_template]); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts index e3e67b0e4dd78..240ce9c469ddb 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts @@ -138,38 +138,23 @@ export const hasPolicyTemplateInputs = ( return policyTemplate.hasOwnProperty('inputs'); }; -interface CloudFormation { - templateUrl: string; - stackName: string; -} - -const emptyCloudFormation: CloudFormation = { - templateUrl: '', - stackName: '', -}; - -export const getVulnMgmtCloudFormation = (packageInfo: PackageInfo): CloudFormation => { - if (!packageInfo.policy_templates) return emptyCloudFormation; +export const getVulnMgmtCloudFormation = (packageInfo: PackageInfo): string => { + if (!packageInfo.policy_templates) return ''; const vulnMgmtPolicyTemplate = packageInfo.policy_templates.find((p) => p.name === 'vuln_mgmt'); - if (!vulnMgmtPolicyTemplate) return emptyCloudFormation; + if (!vulnMgmtPolicyTemplate) return ''; const vulnMgmtInputs = hasPolicyTemplateInputs(vulnMgmtPolicyTemplate) && vulnMgmtPolicyTemplate.inputs; - if (!vulnMgmtInputs) return emptyCloudFormation; - if (!vulnMgmtInputs[0].vars) return emptyCloudFormation; + if (!vulnMgmtInputs) return ''; + if (!vulnMgmtInputs[0].vars) return ''; const cloudFormationTemplate = vulnMgmtInputs[0].vars.find( (v) => v.name === 'cloud_formation_template' ); - const cloudFormationStackName = vulnMgmtInputs[0].vars.find( - (v) => v.name === 'cloud_formation_stack_name' - ); - return { - templateUrl: String(cloudFormationTemplate?.default || ''), - stackName: String(cloudFormationStackName?.default || 'Elastic-Vulnerability-Management'), - }; + + return String(cloudFormationTemplate?.default || ''); }; /** From 06a9956a7cd8349c6c2272d2e1d5b305a0f19295 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 09:42:05 -0300 Subject: [PATCH 14/31] refactor: allow cloud formation to be more generic --- .../fleet/common/types/models/agent_policy.ts | 1 - .../agent_policy_advanced_fields/index.tsx | 38 +------------------ .../agent_policy_validation.test.tsx | 7 ---- .../components/agent_policy_validation.tsx | 12 ------ .../install_agent/install_agent_managed.tsx | 10 +---- .../components/settings/index.tsx | 1 - .../install_section.tsx | 3 ++ .../public/components/platform_selector.tsx | 4 +- .../fleet/server/saved_objects/index.ts | 1 - .../fleet/server/types/models/agent_policy.ts | 7 ---- 10 files changed, 7 insertions(+), 77 deletions(-) diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index a901d1f3fb290..c4cbb1d34c2b8 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -33,7 +33,6 @@ export interface NewAgentPolicy { fleet_server_host_id?: string | null; schema_version?: string; agent_features?: Array<{ name: string; enabled: boolean }>; - cloud_formation_stack_name?: string; cloud_formation_template_url?: string; } 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 bfa8e06a12a0e..6fd7be9476679 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 @@ -674,13 +674,13 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent = label={ } helpText={ } > @@ -702,40 +702,6 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent = )} /> - - } - > - updateAgentPolicy({ cloud_formation_stack_name: e.target.value })} - isInvalid={Boolean( - touchedFields.cloud_formation_stack_name && validation.cloud_formation_stack_name - )} - onBlur={() => setTouchedFields({ ...touchedFields, cloud_formation_stack_name: true })} - placeholder={i18n.translate( - 'xpack.fleet.agentPolicyForm.cloudFormationStackNameFieldPlaceholder', - { - defaultMessage: 'Optional CloudFormation Stack Name', - } - )} - /> - {isEditing && 'id' in agentPolicy && !agentPolicy.is_managed ? ( { }); expect(result.inactivity_timeout).toBeDefined(); }); - it('should return error when CloudFormation template is set with empty Stack Name', () => { - const result = agentPolicyFormValidation({ - cloud_formation_template_url: 'some-url', - cloud_formation_stack_name: '', - }); - expect(result.cloud_formation_stack_name).toBeDefined(); - }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx index 13a1162b29b49..4ba769c311523 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx @@ -51,17 +51,5 @@ export const agentPolicyFormValidation = ( ]; } - if ( - agentPolicy.cloud_formation_template_url !== '' && - agentPolicy.cloud_formation_stack_name === '' - ) { - errors.cloud_formation_stack_name = [ - , - ]; - } - return errors; }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx index 991efdd37716e..3d1afc6a6e46c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx @@ -19,8 +19,6 @@ import { } from '../../../../../../../../../components/agent_enrollment_flyout/steps'; import { ManualInstructions } from '../../../../../../../../../components/enrollment_instructions'; -import type { CloudFormation } from '../../../../../../../../../components/agent_enrollment_flyout/types'; - import { KubernetesManifestApplyStep } from '../../../../../../../../../components/agent_enrollment_flyout/steps/run_k8s_apply_command_step'; import type { InstallAgentPageProps } from './types'; @@ -64,13 +62,7 @@ export const InstallElasticAgentManagedPageStep: React.FC const isK8s = props.packageInfo.name === 'kubernetes' ? 'IS_KUBERNETES_MULTIPAGE' : 'IS_NOT_KUBERNETES'; - const cloudFormation: CloudFormation | undefined = - agentPolicy?.cloud_formation_template_url && agentPolicy?.cloud_formation_stack_name - ? { - templateUrl: agentPolicy.cloud_formation_template_url, - stackName: agentPolicy.cloud_formation_stack_name, - } - : undefined; + const cloudFormation = agentPolicy?.cloud_formation_template_url; const installManagedCommands = ManualInstructions({ apiKey: enrollmentAPIKey.api_key, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx index 1bcdc8fcd0b90..e6bd4b43f5e7d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx @@ -54,7 +54,6 @@ const pickAgentPolicyKeysToSend = (agentPolicy: AgentPolicy) => 'fleet_server_host_id', 'agent_features', 'cloud_formation_template_url', - 'cloud_formation_stack_name', ]); const FormWrapper = styled.div` 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 07877eec1e80e..d5a0f6bfb93c8 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 @@ -21,6 +21,7 @@ interface Props { fullCopyButton?: boolean; isManaged?: boolean; onCopy?: () => void; + cloudFormation?: string; } export const InstallSection: React.FunctionComponent = ({ @@ -30,6 +31,7 @@ export const InstallSection: React.FunctionComponent = ({ fullCopyButton = false, isManaged = true, onCopy, + cloudFormation, }) => { return ( <> @@ -47,6 +49,7 @@ export const InstallSection: React.FunctionComponent = ({ hasK8sIntegrationMultiPage={isK8s === 'IS_KUBERNETES_MULTIPAGE'} isManaged={isManaged} enrollToken={enrollToken} + cloudFormation={cloudFormation} /> ); diff --git a/x-pack/plugins/fleet/public/components/platform_selector.tsx b/x-pack/plugins/fleet/public/components/platform_selector.tsx index f3ba1ae23a548..264a4b73d94e6 100644 --- a/x-pack/plugins/fleet/public/components/platform_selector.tsx +++ b/x-pack/plugins/fleet/public/components/platform_selector.tsx @@ -25,8 +25,6 @@ import { REDUCED_PLATFORM_OPTIONS, PLATFORM_OPTIONS, usePlatform } from '../hook import { KubernetesInstructions } from './agent_enrollment_flyout/kubernetes_instructions'; import { CloudFormationInstructions } from './agent_enrollment_flyout/cloud_formation_instructions'; -import type { CloudFormation } from './agent_enrollment_flyout/types'; - interface Props { linuxCommand: string; macCommand: string; @@ -41,7 +39,7 @@ interface Props { enrollToken?: string | undefined; fullCopyButton?: boolean; onCopy?: () => void; - cloudFormation?: CloudFormation; + cloudFormation?: string; } // Otherwise the copy button is over the text diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index dc5f2ae33905c..ae739d8c241cf 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -122,7 +122,6 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, }, cloud_formation_template_url: { type: 'text' }, - cloud_formation_stack_name: { type: 'text' }, }, }, migrations: { diff --git a/x-pack/plugins/fleet/server/types/models/agent_policy.ts b/x-pack/plugins/fleet/server/types/models/agent_policy.ts index 0a413a2cffe67..6bcb2a0893427 100644 --- a/x-pack/plugins/fleet/server/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/agent_policy.ts @@ -48,13 +48,6 @@ export const AgentPolicyBaseSchema = { ) ), cloud_formation_template_url: schema.maybe(schema.nullable(schema.string())), - // cloud_formation_stack_name: schema.maybe(schema.nullable(schema.string())), - cloud_formation_stack_name: schema.conditional( - schema.siblingRef('cloud_formation_template_url'), - schema.string({ minLength: 1 }), - schema.string({ minLength: 1 }), - schema.maybe(schema.nullable(schema.string())) - ), }; export const NewAgentPolicySchema = schema.object({ From 912efc608050813234dff51c5a7f20f4e6b49a83 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 09:43:16 -0300 Subject: [PATCH 15/31] CloudFormation agent enrolment instructions --- .../cloud_formation_instructions.tsx | 107 ++++++++---------- .../steps/compute_steps.tsx | 3 + .../steps/install_managed_agent_step.tsx | 3 + .../enrollment_instructions/manual/index.tsx | 1 + .../standalone/index.tsx | 1 + 5 files changed, 58 insertions(+), 57 deletions(-) diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx index 15b3bd0cbed76..2b0b17ecd8b3c 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx @@ -6,100 +6,93 @@ */ import React, { useEffect, useState } from 'react'; -import { - EuiText, - EuiButton, - EuiSpacer, - EuiFlexGroup, - EuiFlexItem, - EuiCopy, - EuiCodeBlock, -} from '@elastic/eui'; +import { EuiButton, EuiSpacer, EuiCallOut } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { useGetSettings, useStartServices } from '../../hooks'; - -import { agentPolicyRouteService } from '../../../common'; - -import { sendGetK8sManifest } from '../../hooks/use_request/k8s'; +import { useGetSettings, useKibanaVersion, useStartServices } from '../../hooks'; interface Props { enrollmentAPIKey?: string; - onCopy?: () => void; - onDownload?: () => void; + cloudFormation: string; } +const createCloudFormationUrl = ( + templateURL: string, + enrollmentToken: string, + fleetUrl: string, + kibanaVersion: string +) => { + const cloudFormationUrl = templateURL + .replace('FLEET_ENROLLMENT_TOKEN', enrollmentToken) + .replace('FLEET_URL', fleetUrl) + .replace('KIBANA_VERSION', kibanaVersion); + + return new URL(cloudFormationUrl).toString(); +}; + export const CloudFormationInstructions: React.FunctionComponent = ({ enrollmentAPIKey, - onCopy, - onDownload, + cloudFormation, }) => { + const [fleetServer, setFleetServer] = useState(); + const [isError, setIsError] = useState(false); + const core = useStartServices(); const settings = useGetSettings(); const { notifications } = core; - const [yaml, setYaml] = useState(''); - const [fleetServer, setFleetServer] = useState(); - const [copyButtonClicked, setCopyButtonClicked] = useState(false); - const [downloadButtonClicked, setDownloadButtonClicked] = useState(false); - - const onCopyButtonClick = (copy: () => void) => { - copy(); - setCopyButtonClicked(true); - if (onCopy) onCopy(); - }; - - const onDownloadButtonClick = (downloadLink: string) => { - setDownloadButtonClicked(true); - if (onDownload) onDownload(); - window.location.href = downloadLink; - }; + const kibanaVersion = useKibanaVersion(); + // Fetch the first fleet server host from the settings useEffect(() => { - async function fetchK8sManifest() { + async function fetchAgentManifest() { try { - const fleetServerHosts = settings.data?.item.fleet_server_hosts; - let host = ''; + const fleetServerHosts = await settings.data?.item.fleet_server_hosts; if (fleetServerHosts !== undefined && fleetServerHosts.length !== 0) { setFleetServer(fleetServerHosts[0]); - host = fleetServerHosts[0]; - } - const query = { fleetServer: host, enrolToken: enrollmentAPIKey }; - const res = await sendGetK8sManifest(query); - if (res.error) { - throw res.error; - } - - if (!res.data) { - throw new Error('No data while fetching agent manifest'); } - - setYaml(res.data.item); } catch (error) { notifications.toasts.addError(error, { - title: i18n.translate('xpack.fleet.agentEnrollment.loadk8sManifestErrorTitle', { - defaultMessage: 'Error while fetching agent manifest', - }), + title: i18n.translate( + 'xpack.fleet.agentEnrollment.cloudFormation.errorLoadingAgentManifest', + { + defaultMessage: 'Error while fetching agent manifest', + } + ), }); + setIsError(true); } } - fetchK8sManifest(); + fetchAgentManifest(); }, [notifications.toasts, enrollmentAPIKey, settings.data?.item.fleet_server_hosts]); - const downloadLink = core.http.basePath.prepend( - `${agentPolicyRouteService.getK8sFullDownloadPath()}?fleetServer=${fleetServer}&enrolToken=${enrollmentAPIKey}` - ); + const cloudFormationUrl = + enrollmentAPIKey && fleetServer + ? createCloudFormationUrl(cloudFormation, enrollmentAPIKey, fleetServer, kibanaVersion) + : ''; return ( <> + + onDownloadButtonClick(downloadLink)} + href={cloudFormationUrl} > = ({ }); const instructionsSteps = useMemo(() => { + const cloudFormation = agentPolicy?.cloud_formation_template_url; + const steps: EuiContainedStepProps[] = !agentPolicy ? [ AgentPolicySelectionStep({ @@ -248,6 +250,7 @@ export const ManagedSteps: React.FunctionComponent = ({ selectedApiKeyId, isK8s, enrollToken, + cloudFormation, }) ); if (selectedApiKeyId && apiKeyData) { 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 7656c40a07f71..9e646f2ba3c39 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 @@ -27,6 +27,7 @@ export const InstallManagedAgentStep = ({ isComplete, fullCopyButton, onCopy, + cloudFormation, }: { selectedApiKeyId?: string; apiKeyData?: GetOneEnrollmentAPIKeyResponse | null; @@ -36,6 +37,7 @@ export const InstallManagedAgentStep = ({ isComplete?: boolean; fullCopyButton?: boolean; onCopy?: () => void; + cloudFormation?: string; }): EuiContainedStepProps => { const nonCompleteStatus = selectedApiKeyId ? undefined : 'disabled'; const status = isComplete ? 'complete' : nonCompleteStatus; @@ -51,6 +53,7 @@ export const InstallManagedAgentStep = ({ enrollToken={enrollToken} onCopy={onCopy} fullCopyButton={fullCopyButton} + cloudFormation={cloudFormation} /> ), }; diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx index ea12526dbd90f..ff94307792f75 100644 --- a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx +++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx @@ -69,5 +69,6 @@ sudo elastic-agent enroll ${enrollArgs} \nsudo systemctl enable elastic-agent \n deb: linuxDebCommand, rpm: linuxRpmCommand, kubernetes: k8sCommand, + cloudFormation: '', }; }; 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 6994cf2a7ebc2..42c1f1366d554 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 @@ -38,5 +38,6 @@ cd elastic-agent-${kibanaVersion}-windows-x86_64 deb: linuxDebCommand, rpm: linuxRpmCommand, kubernetes: k8sCommand, + cloudFormation: '', }; }; From 52800fbcc911a4363f8864a0b185e1814869c1f8 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 10:51:39 -0300 Subject: [PATCH 16/31] update text --- .../agent_enrollment_flyout/cloud_formation_instructions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx index 2b0b17ecd8b3c..990a7f989a339 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx @@ -78,7 +78,7 @@ export const CloudFormationInstructions: React.FunctionComponent = ({ Date: Mon, 17 Apr 2023 11:45:23 -0300 Subject: [PATCH 17/31] fix optional types --- .../components/fleet_extensions/policy_template_form.tsx | 6 +++--- x-pack/plugins/fleet/common/types/models/agent_policy.ts | 2 +- x-pack/plugins/fleet/public/types/ui_extensions.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx index 4fab1f1016150..1c520e722d95b 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx @@ -318,7 +318,7 @@ const useCloudFormationTemplate = ({ }: { input: NewPackagePolicyPostureInput; agentPolicy?: AgentPolicy; - onChangeAgentPolicy: (agentPolicy: AgentPolicy) => void; + onChangeAgentPolicy?: (agentPolicy: AgentPolicy) => void; packageInfo: PackageInfo; }) => { useEffect(() => { @@ -326,7 +326,7 @@ const useCloudFormationTemplate = ({ if (input.policy_template !== VULN_MGMT_POLICY_TEMPLATE) { // Clear CloudFormation template URL and stack name // when switching to a non-Vuln Mgmt policy template - onChangeAgentPolicy({ + onChangeAgentPolicy?.({ ...agentPolicy, cloud_formation_template_url: '', }); @@ -336,7 +336,7 @@ const useCloudFormationTemplate = ({ if (templateUrl === '') return; if (agentPolicy?.cloud_formation_template_url === templateUrl) return; - onChangeAgentPolicy({ + onChangeAgentPolicy?.({ ...agentPolicy, cloud_formation_template_url: templateUrl, }); diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index c4cbb1d34c2b8..b3df38193aa6e 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -33,7 +33,7 @@ export interface NewAgentPolicy { fleet_server_host_id?: string | null; schema_version?: string; agent_features?: Array<{ name: string; enabled: boolean }>; - cloud_formation_template_url?: string; + cloud_formation_template_url?: string | null; } export interface AgentPolicy extends Omit { diff --git a/x-pack/plugins/fleet/public/types/ui_extensions.ts b/x-pack/plugins/fleet/public/types/ui_extensions.ts index f2c1e2350359b..cc6f2e61d368a 100644 --- a/x-pack/plugins/fleet/public/types/ui_extensions.ts +++ b/x-pack/plugins/fleet/public/types/ui_extensions.ts @@ -36,7 +36,7 @@ export type PackagePolicyReplaceDefineStepExtensionComponentProps = ( validationResults?: PackagePolicyValidationResults; agentPolicy?: AgentPolicy; packageInfo: PackageInfo; - onChangeAgentPolicy: (newAgentPolicy: Partial) => void; + onChangeAgentPolicy?: (newAgentPolicy: Partial) => void; }; /** From c1410aa854e54d69e0c5382da5a3bca9cdb69e34 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 17 Apr 2023 12:15:52 -0300 Subject: [PATCH 18/31] fixing types --- .../components/agent_policy_advanced_fields/index.tsx | 2 +- .../steps/install_managed_agent_step.tsx | 2 +- .../components/enrollment_instructions/install_section.tsx | 2 +- x-pack/plugins/fleet/public/components/platform_selector.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) 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 6fd7be9476679..dd3c937bc52aa 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 @@ -686,7 +686,7 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent = > updateAgentPolicy({ cloud_formation_template_url: e.target.value })} isInvalid={Boolean( touchedFields.cloud_formation_template_url && validation.cloud_formation_template_url 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 9e646f2ba3c39..dbd5bfc84ac32 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 @@ -37,7 +37,7 @@ export const InstallManagedAgentStep = ({ isComplete?: boolean; fullCopyButton?: boolean; onCopy?: () => void; - cloudFormation?: string; + cloudFormation?: string | null; }): EuiContainedStepProps => { const nonCompleteStatus = selectedApiKeyId ? undefined : 'disabled'; const status = isComplete ? 'complete' : nonCompleteStatus; 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 d5a0f6bfb93c8..f1b12efdd6036 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 @@ -21,7 +21,7 @@ interface Props { fullCopyButton?: boolean; isManaged?: boolean; onCopy?: () => void; - cloudFormation?: string; + cloudFormation?: string | null; } export const InstallSection: React.FunctionComponent = ({ diff --git a/x-pack/plugins/fleet/public/components/platform_selector.tsx b/x-pack/plugins/fleet/public/components/platform_selector.tsx index 264a4b73d94e6..b5205a6cf83e9 100644 --- a/x-pack/plugins/fleet/public/components/platform_selector.tsx +++ b/x-pack/plugins/fleet/public/components/platform_selector.tsx @@ -39,7 +39,7 @@ interface Props { enrollToken?: string | undefined; fullCopyButton?: boolean; onCopy?: () => void; - cloudFormation?: string; + cloudFormation?: string | null; } // Otherwise the copy button is over the text From 0386176d4e3cb30773b9e95ecfc1b2c458330c3d Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Thu, 20 Apr 2023 00:58:16 -0700 Subject: [PATCH 19/31] reverting cloudformation from advanced agent policy --- .../agent_policy_advanced_fields/index.tsx | 62 ------------------- 1 file changed, 62 deletions(-) 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 dd3c937bc52aa..407352a69bebf 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 @@ -642,67 +642,6 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent = /> - - -   - - - } - description={ - - } - > - - } - helpText={ - - } - > - updateAgentPolicy({ cloud_formation_template_url: e.target.value })} - isInvalid={Boolean( - touchedFields.cloud_formation_template_url && validation.cloud_formation_template_url - )} - onBlur={() => - setTouchedFields({ ...touchedFields, cloud_formation_template_url: true }) - } - placeholder={i18n.translate( - 'xpack.fleet.agentPolicyForm.cloudFormationTemplateFieldPlaceholder', - { - defaultMessage: 'CloudFormation template URL', - } - )} - /> - - {isEditing && 'id' in agentPolicy && !agentPolicy.is_managed ? ( = } /> ) : null} - ); }; From cb9a90aac21dc7d48f0283fb6274bde855346887 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Thu, 20 Apr 2023 01:16:16 -0700 Subject: [PATCH 20/31] update snapshot --- .../migrations/group2/check_registered_types.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts index 1da0a3c83c9bb..cca4b3e2ce119 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts @@ -102,7 +102,7 @@ describe('checking migration metadata changes on all registered SO types', () => "index-pattern": "cd51191712081278c2af83d16552c3438ef83353", "infrastructure-monitoring-log-view": "8040108f02ef27419cff79077384379709d44bbc", "infrastructure-ui-source": "2311f7d0abe2a713aa71e30ee24f78828d4acfc1", - "ingest-agent-policies": "e5bb18f8c1d1106139e82fccb93fce01b21fde9b", + "ingest-agent-policies": "3ce1a62e91c8376f3b6d111a052a39a6a1f1277a", "ingest-download-sources": "95a15b6589ef46e75aca8f7e534c493f99cc3ccd", "ingest-outputs": "f5adeb3f6abc732a6067137e170578dbf1f58c62", "ingest-package-policies": "6dc1c9b80a8dc95fbc9c6d9b73dfc56a098eb440", From 0d3e737c686b85ff67b78f4aeba62ec29c62e9b7 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Thu, 20 Apr 2023 09:45:03 -0700 Subject: [PATCH 21/31] remove unnecessary mapping --- .../migrations/group2/check_registered_types.test.ts | 2 +- x-pack/plugins/fleet/server/saved_objects/index.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts index cca4b3e2ce119..1da0a3c83c9bb 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts @@ -102,7 +102,7 @@ describe('checking migration metadata changes on all registered SO types', () => "index-pattern": "cd51191712081278c2af83d16552c3438ef83353", "infrastructure-monitoring-log-view": "8040108f02ef27419cff79077384379709d44bbc", "infrastructure-ui-source": "2311f7d0abe2a713aa71e30ee24f78828d4acfc1", - "ingest-agent-policies": "3ce1a62e91c8376f3b6d111a052a39a6a1f1277a", + "ingest-agent-policies": "e5bb18f8c1d1106139e82fccb93fce01b21fde9b", "ingest-download-sources": "95a15b6589ef46e75aca8f7e534c493f99cc3ccd", "ingest-outputs": "f5adeb3f6abc732a6067137e170578dbf1f58c62", "ingest-package-policies": "6dc1c9b80a8dc95fbc9c6d9b73dfc56a098eb440", diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index ae739d8c241cf..8f0db94d1d31c 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -121,7 +121,6 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ enabled: { type: 'boolean' }, }, }, - cloud_formation_template_url: { type: 'text' }, }, }, migrations: { From 9ae0bd9c43d87a5b864a7b5e460cbec1231dcc98 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Fri, 21 Apr 2023 13:33:04 -0700 Subject: [PATCH 22/31] reverting ui extension updatepackagepolicy --- x-pack/plugins/fleet/public/types/ui_extensions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/types/ui_extensions.ts b/x-pack/plugins/fleet/public/types/ui_extensions.ts index cc6f2e61d368a..53ae5322f0d9d 100644 --- a/x-pack/plugins/fleet/public/types/ui_extensions.ts +++ b/x-pack/plugins/fleet/public/types/ui_extensions.ts @@ -36,7 +36,6 @@ export type PackagePolicyReplaceDefineStepExtensionComponentProps = ( validationResults?: PackagePolicyValidationResults; agentPolicy?: AgentPolicy; packageInfo: PackageInfo; - onChangeAgentPolicy?: (newAgentPolicy: Partial) => void; }; /** From 12c1127554e22977d2b020198e30239db6578165 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Fri, 21 Apr 2023 13:33:46 -0700 Subject: [PATCH 23/31] set cloud formation to config inputs --- .../fleet_extensions/policy_template_form.tsx | 65 +++++++++---------- .../components/fleet_extensions/utils.ts | 4 +- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx index 0b9017b495a37..8bcf4c8a97bbc 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx @@ -20,7 +20,7 @@ import type { NewPackagePolicyInput, PackagePolicyReplaceDefineStepExtensionComponentProps, } from '@kbn/fleet-plugin/public/types'; -import type { AgentPolicy, PackageInfo } from '@kbn/fleet-plugin/common'; +import type { PackageInfo } from '@kbn/fleet-plugin/common'; import { useParams } from 'react-router-dom'; import type { PostureInput, CloudSecurityPolicyTemplate } from '../../../common/types'; import { @@ -29,7 +29,6 @@ import { CLOUDBEAT_VULN_MGMT_AWS, CSPM_POLICY_TEMPLATE, SUPPORTED_POLICY_TEMPLATES, - VULN_MGMT_POLICY_TEMPLATE, } from '../../../common/constants'; import { getPosturePolicy, @@ -88,15 +87,7 @@ const IntegrationSettings = ({ onChange, fields }: IntegrationInfoFieldsProps) = ); export const CspPolicyTemplateForm = memo( - ({ - newPolicy, - onChange, - validationResults, - isEditPage, - packageInfo, - agentPolicy, - onChangeAgentPolicy, - }) => { + ({ newPolicy, onChange, validationResults, isEditPage, packageInfo }) => { const integrationParam = useParams<{ integration: CloudSecurityPolicyTemplate }>().integration; const integration = SUPPORTED_POLICY_TEMPLATES.includes(integrationParam) ? integrationParam @@ -156,7 +147,11 @@ export const CspPolicyTemplateForm = memo void; packageInfo: PackageInfo; + newPolicy: NewPackagePolicy; + updatePolicy: (policy: NewPackagePolicy) => void; }) => { useEffect(() => { - // CloudFormation currently only supported by Vuln Mgmt - if (input.policy_template !== VULN_MGMT_POLICY_TEMPLATE) { - // Clear CloudFormation template URL and stack name - // when switching to a non-Vuln Mgmt policy template - onChangeAgentPolicy?.({ - ...agentPolicy, - cloud_formation_template_url: '', - }); - return; - } const templateUrl = getVulnMgmtCloudFormation(packageInfo); + // If the template is not available, do not update the policy if (templateUrl === '') return; - if (agentPolicy?.cloud_formation_template_url === templateUrl) return; - onChangeAgentPolicy?.({ - ...agentPolicy, - cloud_formation_template_url: templateUrl, + + const checkCurrentTemplate = newPolicy?.inputs?.find( + (i: any) => i.type === CLOUDBEAT_VULN_MGMT_AWS + )?.config?.cloud_formation_template_url?.value; + + // If the template is already set, do not update the policy + if (checkCurrentTemplate === templateUrl) return; + + updatePolicy?.({ + ...newPolicy, + inputs: newPolicy.inputs.map((input) => { + if (input.type === CLOUDBEAT_VULN_MGMT_AWS) { + return { + ...input, + config: { cloud_formation_template_url: { value: templateUrl } }, + }; + } + return input; + }), }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [agentPolicy?.cloud_formation_template_url, input.policy_template]); + }, [newPolicy?.vars?.cloud_formation_template_url, newPolicy, packageInfo]); }; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts index 240ce9c469ddb..907b7f78a7b00 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts @@ -141,7 +141,9 @@ export const hasPolicyTemplateInputs = ( export const getVulnMgmtCloudFormation = (packageInfo: PackageInfo): string => { if (!packageInfo.policy_templates) return ''; - const vulnMgmtPolicyTemplate = packageInfo.policy_templates.find((p) => p.name === 'vuln_mgmt'); + const vulnMgmtPolicyTemplate = packageInfo.policy_templates.find( + (p) => p.name === VULN_MGMT_POLICY_TEMPLATE + ); if (!vulnMgmtPolicyTemplate) return ''; const vulnMgmtInputs = From ac339614dcb36c3ba1dad6df5e1a6ae06caf3fa9 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Fri, 21 Apr 2023 13:35:24 -0700 Subject: [PATCH 24/31] removing cloudFormation from AgentPolicy schema --- x-pack/plugins/fleet/common/types/models/agent_policy.ts | 1 - .../page_steps/install_agent/install_agent_managed.tsx | 3 --- x-pack/plugins/fleet/server/types/models/agent_policy.ts | 1 - 3 files changed, 5 deletions(-) diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index b3df38193aa6e..2f0f5adae5978 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -33,7 +33,6 @@ export interface NewAgentPolicy { fleet_server_host_id?: string | null; schema_version?: string; agent_features?: Array<{ name: string; enabled: boolean }>; - cloud_formation_template_url?: string | null; } export interface AgentPolicy extends Omit { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx index 3d1afc6a6e46c..8b4ff121f3e2a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/install_agent/install_agent_managed.tsx @@ -62,8 +62,6 @@ export const InstallElasticAgentManagedPageStep: React.FC const isK8s = props.packageInfo.name === 'kubernetes' ? 'IS_KUBERNETES_MULTIPAGE' : 'IS_NOT_KUBERNETES'; - const cloudFormation = agentPolicy?.cloud_formation_template_url; - const installManagedCommands = ManualInstructions({ apiKey: enrollmentAPIKey.api_key, fleetProxy, @@ -81,7 +79,6 @@ export const InstallElasticAgentManagedPageStep: React.FC isComplete: commandCopied || !!enrolledAgentIds.length, fullCopyButton: true, onCopy: () => setCommandCopied(true), - cloudFormation, }), ]; diff --git a/x-pack/plugins/fleet/server/types/models/agent_policy.ts b/x-pack/plugins/fleet/server/types/models/agent_policy.ts index 6bcb2a0893427..f85bd93b1e870 100644 --- a/x-pack/plugins/fleet/server/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/agent_policy.ts @@ -47,7 +47,6 @@ export const AgentPolicyBaseSchema = { }) ) ), - cloud_formation_template_url: schema.maybe(schema.nullable(schema.string())), }; export const NewAgentPolicySchema = schema.object({ From bd353e0215b6d85047f9faac6f44026041c29b31 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Fri, 21 Apr 2023 13:35:45 -0700 Subject: [PATCH 25/31] add get cloud formation from package policy --- ...mation_template_url_from_package_policy.ts | 35 +++++++++++++++++++ x-pack/plugins/fleet/public/services/index.ts | 1 + 2 files changed, 36 insertions(+) create mode 100644 x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_policy.ts diff --git a/x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_policy.ts b/x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_policy.ts new file mode 100644 index 0000000000000..0cff589996984 --- /dev/null +++ b/x-pack/plugins/fleet/public/services/get_cloud_formation_template_url_from_package_policy.ts @@ -0,0 +1,35 @@ +/* + * 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 { AgentPolicy } from '../types'; + +/** + * Get the cloud formation template url from a package policy + * It looks for a config with a cloud_formation_template_url object present in + * the enabled inputs of the package policy + */ +export const getCloudFormationTemplateUrlFromPackagePolicy = (selectedPolicy?: AgentPolicy) => { + const cloudFormationTemplateUrl = selectedPolicy?.package_policies?.reduce( + (acc, packagePolicy) => { + const findCloudFormationTemplateUrlConfig = packagePolicy.inputs?.reduce( + (accInput, input) => { + if (input?.enabled && input?.config?.cloud_formation_template_url) { + return input.config.cloud_formation_template_url.value; + } + return accInput; + }, + '' + ); + if (findCloudFormationTemplateUrlConfig) { + return findCloudFormationTemplateUrlConfig; + } + return acc; + }, + '' + ); + return cloudFormationTemplateUrl !== '' ? cloudFormationTemplateUrl : undefined; +}; diff --git a/x-pack/plugins/fleet/public/services/index.ts b/x-pack/plugins/fleet/public/services/index.ts index 6961069ebdce7..8a71f7d96e1fa 100644 --- a/x-pack/plugins/fleet/public/services/index.ts +++ b/x-pack/plugins/fleet/public/services/index.ts @@ -49,3 +49,4 @@ export { pkgKeyFromPackageInfo } from './pkg_key_from_package_info'; export { createExtensionRegistrationCallback } from './ui_extensions'; export { incrementPolicyName } from './increment_policy_name'; export { generateNewAgentPolicyWithDefaults } from './generate_new_agent_policy'; +export { getCloudFormationTemplateUrlFromPackagePolicy } from './get_cloud_formation_template_url_from_package_policy'; From 6192603fb2a95834fd1f81ab6754ab1d2fb74677 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Fri, 21 Apr 2023 13:36:33 -0700 Subject: [PATCH 26/31] change cloudFormation to cloudFormationTemplateUrl --- .../single_page_layout/index.tsx | 1 - .../details_page/components/settings/index.tsx | 1 - .../agent_enrollment_flyout/steps/compute_steps.tsx | 10 +++++++--- .../steps/install_managed_agent_step.tsx | 6 +++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx index e11ab3d2b6b59..81c1c518ccd4f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx @@ -301,7 +301,6 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ onChange={handleExtensionViewOnChange} validationResults={validationResults} isEditPage={false} - onChangeAgentPolicy={updateNewAgentPolicy} /> ) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx index e6bd4b43f5e7d..6080f8cd80710 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx @@ -53,7 +53,6 @@ const pickAgentPolicyKeysToSend = (agentPolicy: AgentPolicy) => 'download_source_id', 'fleet_server_host_id', 'agent_features', - 'cloud_formation_template_url', ]); const FormWrapper = styled.div` diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx index acdccbb7b6d57..15bf9359316ad 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx @@ -14,7 +14,11 @@ import type { EuiContainedStepProps } from '@elastic/eui/src/components/steps/st import type { FullAgentPolicy } from '../../../../common/types/models/agent_policy'; -import { fullAgentPolicyToYaml, agentPolicyRouteService } from '../../../services'; +import { + fullAgentPolicyToYaml, + agentPolicyRouteService, + getCloudFormationTemplateUrlFromPackagePolicy, +} from '../../../services'; import { StandaloneInstructions, ManualInstructions } from '../../enrollment_instructions'; @@ -216,7 +220,7 @@ export const ManagedSteps: React.FunctionComponent = ({ }); const instructionsSteps = useMemo(() => { - const cloudFormation = agentPolicy?.cloud_formation_template_url; + const cloudFormationTemplateUrl = getCloudFormationTemplateUrlFromPackagePolicy(selectedPolicy); const steps: EuiContainedStepProps[] = !agentPolicy ? [ @@ -250,7 +254,7 @@ export const ManagedSteps: React.FunctionComponent = ({ selectedApiKeyId, isK8s, enrollToken, - cloudFormation, + cloudFormationTemplateUrl, }) ); if (selectedApiKeyId && apiKeyData) { 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 dbd5bfc84ac32..997efe59c8fe6 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 @@ -27,7 +27,7 @@ export const InstallManagedAgentStep = ({ isComplete, fullCopyButton, onCopy, - cloudFormation, + cloudFormationTemplateUrl, }: { selectedApiKeyId?: string; apiKeyData?: GetOneEnrollmentAPIKeyResponse | null; @@ -37,7 +37,7 @@ export const InstallManagedAgentStep = ({ isComplete?: boolean; fullCopyButton?: boolean; onCopy?: () => void; - cloudFormation?: string | null; + cloudFormationTemplateUrl?: string | null; }): EuiContainedStepProps => { const nonCompleteStatus = selectedApiKeyId ? undefined : 'disabled'; const status = isComplete ? 'complete' : nonCompleteStatus; @@ -53,7 +53,7 @@ export const InstallManagedAgentStep = ({ enrollToken={enrollToken} onCopy={onCopy} fullCopyButton={fullCopyButton} - cloudFormation={cloudFormation} + cloudFormationTemplateUrl={cloudFormationTemplateUrl} /> ), }; From a833b7e7bed648f03ef130d35f7fbba12af929a2 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Fri, 21 Apr 2023 13:37:07 -0700 Subject: [PATCH 27/31] refactor cloudFormation instructions --- .../cloud_formation_instructions.tsx | 55 ++++++++++--------- .../install_section.tsx | 6 +- .../public/components/platform_selector.tsx | 28 ++++++---- .../fleet/public/hooks/use_platform.tsx | 4 +- 4 files changed, 50 insertions(+), 43 deletions(-) diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx index 990a7f989a339..664b3747618df 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx @@ -14,7 +14,7 @@ import { useGetSettings, useKibanaVersion, useStartServices } from '../../hooks' interface Props { enrollmentAPIKey?: string; - cloudFormation: string; + cloudFormationTemplateUrl: string; } const createCloudFormationUrl = ( @@ -33,43 +33,46 @@ const createCloudFormationUrl = ( export const CloudFormationInstructions: React.FunctionComponent = ({ enrollmentAPIKey, - cloudFormation, + cloudFormationTemplateUrl, }) => { const [fleetServer, setFleetServer] = useState(); const [isError, setIsError] = useState(false); const core = useStartServices(); - const settings = useGetSettings(); + const { data, isLoading } = useGetSettings(); const { notifications } = core; const kibanaVersion = useKibanaVersion(); - // Fetch the first fleet server host from the settings + // Sets Fleet Server Host as the first fleet server host available from the settings + // Shows an error if no fleet server host is available useEffect(() => { - async function fetchAgentManifest() { - try { - const fleetServerHosts = await settings.data?.item.fleet_server_hosts; - if (fleetServerHosts !== undefined && fleetServerHosts.length !== 0) { - setFleetServer(fleetServerHosts[0]); - } - } catch (error) { - notifications.toasts.addError(error, { - title: i18n.translate( - 'xpack.fleet.agentEnrollment.cloudFormation.errorLoadingAgentManifest', - { - defaultMessage: 'Error while fetching agent manifest', - } - ), - }); - setIsError(true); - } + if (isLoading) return; + + const fleetServerHosts = data?.item.fleet_server_hosts; + if (fleetServerHosts !== undefined && fleetServerHosts.length !== 0) { + setFleetServer(fleetServerHosts[0]); + } else { + setIsError(true); + notifications.toasts.addError(new Error('Fleet server host not found'), { + title: i18n.translate( + 'xpack.fleet.agentEnrollment.cloudFormation.errorLoadingFleetServerHosts', + { + defaultMessage: 'Error while fetching Fleet Server hosts', + } + ), + }); } - fetchAgentManifest(); - }, [notifications.toasts, enrollmentAPIKey, settings.data?.item.fleet_server_hosts]); + }, [data?.item.fleet_server_hosts, isLoading, notifications.toasts]); const cloudFormationUrl = enrollmentAPIKey && fleetServer - ? createCloudFormationUrl(cloudFormation, enrollmentAPIKey, fleetServer, kibanaVersion) + ? createCloudFormationUrl( + cloudFormationTemplateUrl, + enrollmentAPIKey, + fleetServer, + kibanaVersion + ) : ''; return ( @@ -78,14 +81,14 @@ export const CloudFormationInstructions: React.FunctionComponent = ({ void; - cloudFormation?: string | null; + cloudFormationTemplateUrl?: string | null; } export const InstallSection: React.FunctionComponent = ({ @@ -31,7 +31,7 @@ export const InstallSection: React.FunctionComponent = ({ fullCopyButton = false, isManaged = true, onCopy, - cloudFormation, + cloudFormationTemplateUrl, }) => { return ( <> @@ -49,7 +49,7 @@ export const InstallSection: React.FunctionComponent = ({ hasK8sIntegrationMultiPage={isK8s === 'IS_KUBERNETES_MULTIPAGE'} isManaged={isManaged} enrollToken={enrollToken} - cloudFormation={cloudFormation} + cloudFormationTemplateUrl={cloudFormationTemplateUrl} /> ); diff --git a/x-pack/plugins/fleet/public/components/platform_selector.tsx b/x-pack/plugins/fleet/public/components/platform_selector.tsx index b5205a6cf83e9..11d2363b81177 100644 --- a/x-pack/plugins/fleet/public/components/platform_selector.tsx +++ b/x-pack/plugins/fleet/public/components/platform_selector.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useCallback } from 'react'; import styled from 'styled-components'; import { EuiText, @@ -39,7 +39,7 @@ interface Props { enrollToken?: string | undefined; fullCopyButton?: boolean; onCopy?: () => void; - cloudFormation?: string | null; + cloudFormationTemplateUrl?: string | null; } // Otherwise the copy button is over the text @@ -61,13 +61,17 @@ export const PlatformSelector: React.FunctionComponent = ({ hasFleetServer, fullCopyButton, onCopy, - cloudFormation, + cloudFormationTemplateUrl, }) => { - const { platform, setPlatform } = usePlatform(); + const getInitialPlatform = useCallback(() => { + if (cloudFormationTemplateUrl) return 'cloudFormation'; - useEffect(() => { - setPlatform(hasK8sIntegration ? 'kubernetes' : 'linux'); - }, [hasK8sIntegration, setPlatform]); + if (hasK8sIntegration) return 'kubernetes'; + + return 'linux'; + }, [cloudFormationTemplateUrl, hasK8sIntegration]); + + const { platform, setPlatform } = usePlatform(getInitialPlatform()); // In case of fleet server installation or standalone agent without // Kubernetes integration in the policy use reduced platform options @@ -76,12 +80,12 @@ export const PlatformSelector: React.FunctionComponent = ({ const getPlatformOptions = useCallback(() => { const platformOptions = isReduced ? REDUCED_PLATFORM_OPTIONS : PLATFORM_OPTIONS; - if (cloudFormation) { + if (cloudFormationTemplateUrl) { return platformOptions.concat(CLOUD_FORMATION_PLATFORM_OPTION); } return platformOptions; - }, [cloudFormation, isReduced]); + }, [cloudFormationTemplateUrl, isReduced]); const [copyButtonClicked, setCopyButtonClicked] = useState(false); @@ -161,16 +165,16 @@ export const PlatformSelector: React.FunctionComponent = ({ )} - {platform === 'cloudFormation' && cloudFormation && ( + {platform === 'cloudFormation' && cloudFormationTemplateUrl && ( <> )} - {!hasK8sIntegrationMultiPage && !cloudFormation && ( + {!hasK8sIntegrationMultiPage && platform !== 'cloudFormation' && ( <> {platform === 'kubernetes' && ( diff --git a/x-pack/plugins/fleet/public/hooks/use_platform.tsx b/x-pack/plugins/fleet/public/hooks/use_platform.tsx index e127440937737..8c739a262d4da 100644 --- a/x-pack/plugins/fleet/public/hooks/use_platform.tsx +++ b/x-pack/plugins/fleet/public/hooks/use_platform.tsx @@ -78,8 +78,8 @@ export const PLATFORM_OPTIONS = [ }, ]; -export function usePlatform() { - const [platform, setPlatform] = useState('linux'); +export function usePlatform(initialPlatform: PLATFORM_TYPE = 'linux') { + const [platform, setPlatform] = useState(initialPlatform); return { platform, From 98d976165893e08b66f3ab1eddfe41beee5c2af7 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Fri, 21 Apr 2023 13:59:50 -0700 Subject: [PATCH 28/31] updating tests --- .../policy_template_form.test.tsx | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx index 5abd218e72ddf..5e2cb31ac520e 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx @@ -35,7 +35,6 @@ jest.mock('react-router-dom', () => ({ })); const onChange = jest.fn(); -const onChangeAgentPolicy = jest.fn(); describe('', () => { beforeEach(() => { @@ -43,7 +42,6 @@ describe('', () => { integration: undefined, }); onChange.mockClear(); - onChangeAgentPolicy.mockClear(); }); const WrappedComponent = ({ @@ -65,7 +63,6 @@ describe('', () => { onChange={onChange} packageInfo={packageInfo} isEditPage={true} - onChangeAgentPolicy={onChangeAgentPolicy} agentPolicy={agentPolicy} /> )} @@ -75,7 +72,6 @@ describe('', () => { onChange={onChange} packageInfo={packageInfo} isEditPage={false} - onChangeAgentPolicy={onChangeAgentPolicy} agentPolicy={agentPolicy} /> )} @@ -158,11 +154,6 @@ describe('', () => { isValid: true, updatedPolicy: eksPolicy, }); - - // make sure CloudFormation template is reset - expect(onChangeAgentPolicy).toHaveBeenCalledWith({ - cloud_formation_template_url: '', - }); }); it('renders CSPM input selector', () => { @@ -179,11 +170,6 @@ describe('', () => { expect(option2).toBeDisabled(); expect(option3).toBeDisabled(); expect(option1).toBeChecked(); - - // make sure CloudFormation template is reset - expect(onChangeAgentPolicy).toHaveBeenCalledWith({ - cloud_formation_template_url: '', - }); }); it('renders disabled KSPM input when editing', () => { @@ -468,13 +454,28 @@ describe('', () => { } describe('Vuln Mgmt', () => { - it('Update Agent Policy CloudFormation template and stack name from vars', () => { + it('Update Agent Policy CloudFormation template from vars', () => { const policy = getMockPolicyVulnMgmtAWS(); + const packageInfo = getMockPackageInfoVulnMgmtAWS(); render(); - expect(onChangeAgentPolicy).toHaveBeenNthCalledWith(1, { - cloud_formation_template_url: 's3_url', + const expectedUpdatedPolicy = { + ...policy, + inputs: policy.inputs.map((input) => { + if (input.type === 'cloudbeat/vuln_mgmt_aws') { + return { + ...input, + config: { cloud_formation_template_url: { value: 's3_url' } }, + }; + } + return input; + }), + }; + + expect(onChange).toHaveBeenNthCalledWith(2, { + isValid: true, + updatedPolicy: expectedUpdatedPolicy, }); }); }); From cce4d419f60270cd58ba23c7a1ca85ba91ad5193 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Fri, 21 Apr 2023 14:03:47 -0700 Subject: [PATCH 29/31] addressing pr suggestions --- .../components/fleet_extensions/policy_template_form.tsx | 4 ++-- .../public/components/fleet_extensions/utils.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx index 8bcf4c8a97bbc..85e6be8905c83 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx @@ -33,7 +33,7 @@ import { import { getPosturePolicy, getPostureInputHiddenVars, - getVulnMgmtCloudFormation, + getVulnMgmtCloudFormationDefaultValue, POSTURE_NAMESPACE, type NewPackagePolicyPostureInput, isPostureInput, @@ -311,7 +311,7 @@ const useCloudFormationTemplate = ({ updatePolicy: (policy: NewPackagePolicy) => void; }) => { useEffect(() => { - const templateUrl = getVulnMgmtCloudFormation(packageInfo); + const templateUrl = getVulnMgmtCloudFormationDefaultValue(packageInfo); // If the template is not available, do not update the policy if (templateUrl === '') return; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts index 907b7f78a7b00..121dca0f5b764 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts @@ -138,7 +138,7 @@ export const hasPolicyTemplateInputs = ( return policyTemplate.hasOwnProperty('inputs'); }; -export const getVulnMgmtCloudFormation = (packageInfo: PackageInfo): string => { +export const getVulnMgmtCloudFormationDefaultValue = (packageInfo: PackageInfo): string => { if (!packageInfo.policy_templates) return ''; const vulnMgmtPolicyTemplate = packageInfo.policy_templates.find( From 2cd1d7d33065127133452ee35d31e8fce88682d8 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Fri, 21 Apr 2023 14:13:20 -0700 Subject: [PATCH 30/31] addressing pr comments --- .../public/components/fleet_extensions/utils.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts index 121dca0f5b764..3827fc7303da7 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts @@ -150,13 +150,14 @@ export const getVulnMgmtCloudFormationDefaultValue = (packageInfo: PackageInfo): hasPolicyTemplateInputs(vulnMgmtPolicyTemplate) && vulnMgmtPolicyTemplate.inputs; if (!vulnMgmtInputs) return ''; - if (!vulnMgmtInputs[0].vars) return ''; - const cloudFormationTemplate = vulnMgmtInputs[0].vars.find( - (v) => v.name === 'cloud_formation_template' - ); + const cloudFormationTemplate = vulnMgmtInputs.reduce((acc, input): string => { + if (!input.vars) return acc; + const template = input.vars.find((v) => v.name === 'cloud_formation_template')?.default; + return template ? String(template) : acc; + }, ''); - return String(cloudFormationTemplate?.default || ''); + return cloudFormationTemplate; }; /** From 5993ebcf6b19454401f25508315aa08f72210f9c Mon Sep 17 00:00:00 2001 From: Paulo Henrique Date: Mon, 24 Apr 2023 22:05:44 -0700 Subject: [PATCH 31/31] removing unneeded useEffect and useState --- .../cloud_formation_instructions.tsx | 92 +++++++++++-------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx index 664b3747618df..153bdbf9ec794 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/cloud_formation_instructions.tsx @@ -5,12 +5,12 @@ * 2.0. */ -import React, { useEffect, useState } from 'react'; -import { EuiButton, EuiSpacer, EuiCallOut } from '@elastic/eui'; +import React from 'react'; +import { EuiButton, EuiSpacer, EuiCallOut, EuiSkeletonText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { useGetSettings, useKibanaVersion, useStartServices } from '../../hooks'; +import { useGetSettings, useKibanaVersion } from '../../hooks'; interface Props { enrollmentAPIKey?: string; @@ -35,48 +35,62 @@ export const CloudFormationInstructions: React.FunctionComponent = ({ enrollmentAPIKey, cloudFormationTemplateUrl, }) => { - const [fleetServer, setFleetServer] = useState(); - const [isError, setIsError] = useState(false); - - const core = useStartServices(); const { data, isLoading } = useGetSettings(); - const { notifications } = core; const kibanaVersion = useKibanaVersion(); - // Sets Fleet Server Host as the first fleet server host available from the settings - // Shows an error if no fleet server host is available - useEffect(() => { - if (isLoading) return; + // Default fleet server host + const fleetServerHost = data?.item.fleet_server_hosts?.[0]; - const fleetServerHosts = data?.item.fleet_server_hosts; - if (fleetServerHosts !== undefined && fleetServerHosts.length !== 0) { - setFleetServer(fleetServerHosts[0]); - } else { - setIsError(true); - notifications.toasts.addError(new Error('Fleet server host not found'), { - title: i18n.translate( - 'xpack.fleet.agentEnrollment.cloudFormation.errorLoadingFleetServerHosts', - { - defaultMessage: 'Error while fetching Fleet Server hosts', - } - ), - }); - } - }, [data?.item.fleet_server_hosts, isLoading, notifications.toasts]); + if (!isLoading && !fleetServerHost) { + return ( + <> + + + + ); + } - const cloudFormationUrl = - enrollmentAPIKey && fleetServer - ? createCloudFormationUrl( - cloudFormationTemplateUrl, - enrollmentAPIKey, - fleetServer, - kibanaVersion - ) - : ''; + if (!enrollmentAPIKey) { + return ( + <> + + + + ); + } + + const cloudFormationUrl = createCloudFormationUrl( + cloudFormationTemplateUrl, + enrollmentAPIKey, + fleetServerHost || '', + kibanaVersion + ); return ( - <> + = ({ /> = ({ defaultMessage="Launch CloudFormation" /> - + ); };