diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx index 6ab01d06f52ddd..dcbd29c1ef74e0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/index.tsx @@ -98,6 +98,7 @@ export const AgentPolicyListPage: React.FunctionComponent<{}> = () => { sortField: sorting?.field, sortOrder: sorting?.direction, kuery: search, + noAgentCount: false, // Explicitly fetch agent count full: true, }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_fetch_agents_data.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_fetch_agents_data.test.tsx index 5640890b3f8107..db4cff14e4804a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_fetch_agents_data.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_fetch_agents_data.test.tsx @@ -21,6 +21,13 @@ jest.mock('../../../../hooks', () => ({ ...jest.requireActual('../../../../hooks'), sendGetAgents: jest.fn().mockResolvedValue({ data: { + statusSummary: {}, + items: [ + { + id: 'agent123', + policy_id: 'agent-policy-1', + }, + ], total: 5, }, }), @@ -32,6 +39,19 @@ jest.mock('../../../../hooks', () => ({ totalInactive: 2, }, }), + sendBulkGetAgentPolicies: jest.fn().mockReturnValue({ + data: { + items: [ + { id: 'agent-policy-1', name: 'Agent policy 1', namespace: 'default' }, + { + id: 'agent-policy-managed', + name: 'Managed Agent policy', + namespace: 'default', + managed: true, + }, + ], + }, + }), sendGetAgentPolicies: jest.fn().mockReturnValue({ data: { items: [ @@ -104,7 +124,7 @@ describe('useFetchAgentsData', () => { }); expect(result?.current.selectedStatus).toEqual(['healthy', 'unhealthy', 'updating', 'offline']); - expect(result?.current.agentPolicies).toEqual([ + expect(result?.current.allAgentPolicies).toEqual([ { id: 'agent-policy-1', name: 'Agent policy 1', @@ -124,17 +144,11 @@ describe('useFetchAgentsData', () => { name: 'Agent policy 1', namespace: 'default', }, - 'agent-policy-managed': { - id: 'agent-policy-managed', - managed: true, - name: 'Managed Agent policy', - namespace: 'default', - }, }); expect(result?.current.kuery).toEqual( 'status:online or (status:error or status:degraded) or (status:updating or status:unenrolling or status:enrolling) or status:offline' ); - expect(result?.current.currentRequestRef).toEqual({ current: 1 }); + expect(result?.current.currentRequestRef).toEqual({ current: 2 }); expect(result?.current.pagination).toEqual({ currentPage: 1, pageSize: 5 }); expect(result?.current.pageSizeOptions).toEqual([5, 20, 50]); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_fetch_agents_data.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_fetch_agents_data.tsx index e67d0e83c28e08..2c9fcfe2b16c67 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_fetch_agents_data.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/hooks/use_fetch_agents_data.tsx @@ -23,6 +23,7 @@ import { sendGetAgentPolicies, useAuthz, sendGetActionStatus, + sendBulkGetAgentPolicies, } from '../../../../hooks'; import { AgentStatusKueryHelper, ExperimentalFeaturesService } from '../../../../services'; import { LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE, SO_SEARCH_LIMIT } from '../../../../constants'; @@ -32,8 +33,59 @@ import { getKuery } from '../utils/get_kuery'; const REFRESH_INTERVAL_MS = 30000; const MAX_AGENT_ACTIONS = 100; -export function useFetchAgentsData() { +/** Allow to fetch full agent policy using a cache */ +function useFullAgentPolicyFetcher() { const authz = useAuthz(); + const fetchedAgentPoliciesRef = useRef<{ + [k: string]: AgentPolicy; + }>({}); + + const fetchPolicies = useCallback( + async (policiesIds: string[]) => { + const policiesToFetchIds = policiesIds.reduce((acc, policyId) => { + if (!fetchedAgentPoliciesRef.current[policyId]) { + acc.push(policyId); + } + return acc; + }, [] as string[]); + + if (policiesToFetchIds.length) { + const bulkGetAgentPoliciesResponse = await sendBulkGetAgentPolicies(policiesToFetchIds, { + full: authz.fleet.readAgentPolicies, + }); + + if (bulkGetAgentPoliciesResponse.error) { + throw bulkGetAgentPoliciesResponse.error; + } + + if (!bulkGetAgentPoliciesResponse.data) { + throw new Error('Invalid bulk GET agent policies response'); + } + bulkGetAgentPoliciesResponse.data.items.forEach((agentPolicy) => { + fetchedAgentPoliciesRef.current[agentPolicy.id] = agentPolicy; + }); + } + + return policiesIds.reduce((acc, policyId) => { + if (fetchedAgentPoliciesRef.current[policyId]) { + acc.push(fetchedAgentPoliciesRef.current[policyId]); + } + return acc; + }, [] as AgentPolicy[]); + }, + [authz.fleet.readAgentPolicies] + ); + + return useMemo( + () => ({ + fetchPolicies, + }), + [fetchPolicies] + ); +} + +export function useFetchAgentsData() { + const fullAgentPolicyFecher = useFullAgentPolicyFetcher(); const { displayAgentMetrics } = ExperimentalFeaturesService.get(); const { notifications } = useStartServices(); @@ -117,6 +169,9 @@ export function useFetchAgentsData() { const [totalInactiveAgents, setTotalInactiveAgents] = useState(0); const [totalManagedAgentIds, setTotalManagedAgentIds] = useState([]); const [managedAgentsOnCurrentPage, setManagedAgentsOnCurrentPage] = useState(0); + const [agentPoliciesIndexedById, setAgentPoliciesIndexedByIds] = useState<{ + [k: string]: AgentPolicy; + }>({}); const [latestAgentActionErrors, setLatestAgentActionErrors] = useState([]); @@ -180,7 +235,6 @@ export function useFetchAgentsData() { }), ]); - isLoadingVar.current = false; // Return if a newer request has been triggered if (currentRequestRef.current !== currentRequest) { return; @@ -211,6 +265,25 @@ export function useFetchAgentsData() { if (!statusSummary) { throw new Error('Invalid GET /agents response - no status summary'); } + // Fetch agent policies, use a local cache + const policyIds = agentsResponse.data.items.map((agent) => agent.policy_id as string); + + const policies = await fullAgentPolicyFecher.fetchPolicies(policyIds); + + isLoadingVar.current = false; + // Return if a newe request has been triggerd + if (currentRequestRef.current !== currentRequest) { + return; + } + + setAgentPoliciesIndexedByIds( + policies.reduce((acc, agentPolicy) => { + acc[agentPolicy.id] = agentPolicy; + + return acc; + }, {} as { [k: string]: AgentPolicy }) + ); + setAgentsStatus(agentStatusesToSummary(statusSummary)); const newAllTags = agentTagsResponse.data.items; @@ -264,6 +337,7 @@ export function useFetchAgentsData() { setLatestAgentActionErrors(allRecentActionErrors); } } catch (error) { + isLoadingVar.current = false; notifications.toasts.addError(error, { title: i18n.translate('xpack.fleet.agentList.errorFetchingDataTitle', { defaultMessage: 'Error fetching agents', @@ -275,6 +349,7 @@ export function useFetchAgentsData() { fetchDataAsync(); }, [ + fullAgentPolicyFecher, pagination.currentPage, pagination.pageSize, kuery, @@ -302,20 +377,12 @@ export function useFetchAgentsData() { const agentPoliciesRequest = useGetAgentPolicies({ page: 1, perPage: SO_SEARCH_LIMIT, - full: authz.fleet.readAgentPolicies, }); - const agentPolicies = useMemo( - () => (agentPoliciesRequest.data ? agentPoliciesRequest.data.items : []), - [agentPoliciesRequest] + const allAgentPolicies = useMemo( + () => agentPoliciesRequest.data?.items || [], + [agentPoliciesRequest.data] ); - const agentPoliciesIndexedById = useMemo(() => { - return agentPolicies.reduce((acc, agentPolicy) => { - acc[agentPolicy.id] = agentPolicy; - - return acc; - }, {} as { [k: string]: AgentPolicy }); - }, [agentPolicies]); return { allTags, @@ -341,7 +408,7 @@ export function useFetchAgentsData() { setSelectedStatus, selectedTags, setSelectedTags, - agentPolicies, + allAgentPolicies, agentPoliciesRequest, agentPoliciesIndexedById, pagination, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.test.tsx index f7b3b1588b31b6..985e709ba22d0e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.test.tsx @@ -47,6 +47,14 @@ jest.mock('../../../hooks', () => ({ }, useFleetStatus: jest.fn().mockReturnValue({}), sendGetAgentStatus: jest.fn(), + sendBulkGetAgentPolicies: jest.fn().mockResolvedValue({ + data: { + items: [ + { id: 'policy1', is_managed: false }, + { id: 'managed_policy', is_managed: true }, + ], + }, + }), sendGetAgentPolicies: jest.fn().mockResolvedValue({ data: { items: [] } }), sendGetAgentTags: jest.fn().mockReturnValue({ data: { items: ['tag1', 'tag2'] } }), useAuthz: jest diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx index c1e1f83f491035..0535bc9c6af628 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/index.tsx @@ -101,7 +101,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { setSelectedStatus, selectedTags, setSelectedTags, - agentPolicies, + allAgentPolicies, agentPoliciesRequest, agentPoliciesIndexedById, pagination, @@ -286,14 +286,14 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { refreshAgentActivity={isLoading} setSearch={setSearch} setSelectedStatus={setSelectedStatus} - agentPolicies={agentPolicies} + agentPolicies={allAgentPolicies} /> ) : null} {enrollmentFlyout.isOpen ? ( p.id === enrollmentFlyout.selectedPolicyId)} + agentPolicy={allAgentPolicies.find((p) => p.id === enrollmentFlyout.selectedPolicyId)} onClose={() => { setEnrollmentFlyoutState({ isOpen: false }); fetchData(); @@ -401,7 +401,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { )} {/* Search and filter bar */} { const authz = useAuthz(); const { - data: agentPolicies, + data: packagePolicies, isLoading: areAgentPoliciesLoading, isFetched: areAgentsFetched, - } = useGetAgentPoliciesQuery( + } = useGetPackagePoliciesQuery( { - full: true, + kuery: `${LEGACY_PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${FLEET_SERVER_PACKAGE}`, }, { enabled: authz.fleet.readAgentPolicies, } ); + const policyIds = [...new Set(packagePolicies?.items.flatMap((item) => item.policy_ids) ?? [])]; + // now get all agents that are NOT part of a fleet server policy - const serverPolicyIdsQuery = (agentPolicies?.items || []) - .filter((item) => policyHasFleetServer(item)) - .map((p) => `policy_id:${p.id}`) - .join(' or '); + const serverPolicyIdsQuery = policyIds.map((policyId) => `policy_id:${policyId}`).join(' or '); // get agents that are not unenrolled and not fleet server const kuery = diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx index ef9f9f34a7eda2..1b4e6565efc018 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx @@ -31,7 +31,7 @@ import { useAuthz, } from '../../hooks'; import { FLEET_SERVER_PACKAGE, MAX_FLYOUT_WIDTH } from '../../constants'; -import type { PackagePolicy, AgentPolicy } from '../../types'; +import type { PackagePolicy } from '../../types'; import { Loading } from '..'; @@ -57,11 +57,6 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ isIntegrationFlow, installedPackagePolicy, }) => { - const findPolicyById = (policies: AgentPolicy[], id: string | undefined) => { - if (!id) return undefined; - return policies.find((p) => p.id === id); - }; - const authz = useAuthz(); const fleetStatus = useFleetStatus(); @@ -87,7 +82,7 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ const selectedPolicy = agentPolicyWithPackagePolicies ? agentPolicyWithPackagePolicies - : findPolicyById(agentPolicies, selectedPolicyId); + : undefined; const hasNoFleetServerHost = fleetStatus.isReady && !fleetServerHost; diff --git a/x-pack/plugins/fleet/public/hooks/use_agent_enrollment_flyout_data.ts b/x-pack/plugins/fleet/public/hooks/use_agent_enrollment_flyout_data.ts index 5066366893b0dd..2472331025510c 100644 --- a/x-pack/plugins/fleet/public/hooks/use_agent_enrollment_flyout_data.ts +++ b/x-pack/plugins/fleet/public/hooks/use_agent_enrollment_flyout_data.ts @@ -10,8 +10,6 @@ import { useMemo } from 'react'; import type { AgentPolicy } from '../types'; import { SO_SEARCH_LIMIT } from '../constants'; -import { useAuthz } from './use_authz'; - import { useGetAgentPolicies, useGetEnrollmentSettings } from './use_request'; interface AgentEnrollmentFlyoutData { @@ -22,7 +20,6 @@ interface AgentEnrollmentFlyoutData { } export function useAgentEnrollmentFlyoutData(): AgentEnrollmentFlyoutData { - const authz = useAuthz(); const { data: agentPoliciesData, isInitialRequest: isInitialAgentPolicyRequest, @@ -31,7 +28,6 @@ export function useAgentEnrollmentFlyoutData(): AgentEnrollmentFlyoutData { } = useGetAgentPolicies({ page: 1, perPage: SO_SEARCH_LIMIT, - full: authz.fleet.readAgentPolicies, }); const { diff --git a/x-pack/plugins/fleet/public/hooks/use_package_installations.tsx b/x-pack/plugins/fleet/public/hooks/use_package_installations.tsx deleted file mode 100644 index 9147d781336de8..00000000000000 --- a/x-pack/plugins/fleet/public/hooks/use_package_installations.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useMemo } from 'react'; -import semverLt from 'semver/functions/lt'; - -import { installationStatuses } from '../../common/constants'; -import type { PackagePolicy } from '../types'; - -import { useGetPackagesQuery } from './use_request/epm'; -import { useGetAgentPoliciesQuery } from './use_request/agent_policy'; - -interface UpdatableIntegration { - currentVersion: string; - policiesToUpgrade: Array<{ - id: string; - name: string; - agentsCount: number; - pkgPolicyId: string; - pkgPolicyName: string; - pkgPolicyIntegrationVersion: string; - }>; -} - -export const usePackageInstallationsQuery = () => { - const { data: allPackages, isLoading: isLoadingPackages } = useGetPackagesQuery({ - prerelease: true, - }); - - const { data: agentPolicyData, isLoading: isLoadingPolicies } = useGetAgentPoliciesQuery({ - full: true, - }); - - const allInstalledPackages = useMemo( - () => (allPackages?.items || []).filter((pkg) => pkg.status === installationStatuses.Installed), - [allPackages?.items] - ); - - const updatablePackages = useMemo( - () => - allInstalledPackages.filter( - (item) => - 'installationInfo' in item && - item.installationInfo?.version && - semverLt(item.installationInfo.version, item.version) - ), - [allInstalledPackages] - ); - - const updatableIntegrations = useMemo>( - () => - (agentPolicyData?.items || []).reduce((result, policy) => { - policy.package_policies?.forEach((pkgPolicy: PackagePolicy) => { - if (!pkgPolicy.package) return false; - const { name, version } = pkgPolicy.package; - const installedPackage = allInstalledPackages.find( - (installedPkg) => - 'installationInfo' in installedPkg && installedPkg?.installationInfo?.name === name - ); - if ( - installedPackage && - 'installationInfo' in installedPackage && - installedPackage?.installationInfo?.version && - semverLt(version, installedPackage.installationInfo.version) - ) { - const packageData = result.get(name) ?? { - currentVersion: installedPackage.installationInfo.version, - policiesToUpgrade: [], - }; - packageData.policiesToUpgrade.push({ - id: policy.id, - name: policy.name, - agentsCount: policy.agents ?? 0, - pkgPolicyId: pkgPolicy.id, - pkgPolicyName: pkgPolicy.name, - pkgPolicyIntegrationVersion: version, - }); - result.set(name, packageData); - } - }); - return result; - }, new Map()), - [allInstalledPackages, agentPolicyData] - ); - - return { - allPackages, - allInstalledPackages, - updatablePackages, - updatableIntegrations, - isLoadingPackages, - isLoadingPolicies, - }; -}; diff --git a/x-pack/plugins/fleet/public/hooks/use_request/agent_policy.ts b/x-pack/plugins/fleet/public/hooks/use_request/agent_policy.ts index 837222dae103fa..b1a76f5a334f2c 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/agent_policy.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/agent_policy.ts @@ -32,7 +32,8 @@ export const useGetAgentPolicies = (query?: GetAgentPoliciesRequest['query']) => return useRequest({ path: agentPolicyRouteService.getListPath(), method: 'get', - query, + // Make noAgentCount the default as it have a significant performance impact + query: { ...(query ?? {}), noAgentCount: query?.noAgentCount === false ? false : true }, version: API_VERSIONS.public.v1, }); }; @@ -47,14 +48,18 @@ export const useGetAgentPoliciesQuery = ( sendRequestForRq({ path: agentPolicyRouteService.getListPath(), method: 'get', - query, + // Make noAgentCount the default as it have a significant performance impact + query: { ...(query ?? {}), noAgentCount: query?.noAgentCount === false ? false : true }, version: API_VERSIONS.public.v1, }), enabled: options?.enabled, }); }; -export const useBulkGetAgentPoliciesQuery = (ids: string[], options?: { full?: boolean }) => { +export const useBulkGetAgentPoliciesQuery = ( + ids: string[], + options?: { full?: boolean; ignoreMissing?: boolean } +) => { return useQuery(['agentPolicies', ids], () => sendRequestForRq({ path: agentPolicyRouteService.getBulkGetPath(), @@ -65,11 +70,14 @@ export const useBulkGetAgentPoliciesQuery = (ids: string[], options?: { full?: b ); }; -export const sendBulkGetAgentPolicies = (ids: string[], options?: { full?: boolean }) => { +export const sendBulkGetAgentPolicies = ( + ids: string[], + options?: { full?: boolean; ignoreMissing?: boolean } +) => { return sendRequest({ path: agentPolicyRouteService.getBulkGetPath(), method: 'post', - body: JSON.stringify({ ids, full: options?.full }), + body: JSON.stringify({ ids, full: options?.full, ignoreMissing: options?.ignoreMissing }), version: API_VERSIONS.public.v1, }); }; diff --git a/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts b/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts index 6ceda885e48d4f..8cacf2de03e230 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts @@ -59,14 +59,20 @@ export const sendDeletePackagePolicy = (body: DeletePackagePoliciesRequest['body }); }; -export function useGetPackagePoliciesQuery(query: GetPackagePoliciesRequest['query']) { - return useQuery(['packagePolicies'], () => - sendRequestForRq({ - method: 'get', - version: API_VERSIONS.public.v1, - path: packagePolicyRouteService.getListPath(), - query, - }) +export function useGetPackagePoliciesQuery( + query: GetPackagePoliciesRequest['query'], + options: Partial<{ enabled: boolean }> = {} +) { + return useQuery( + ['packagePolicies'], + () => + sendRequestForRq({ + method: 'get', + version: API_VERSIONS.public.v1, + path: packagePolicyRouteService.getListPath(), + query, + }), + options ); } diff --git a/x-pack/plugins/fleet/scripts/create_agent_policies/create_agent_policies.ts b/x-pack/plugins/fleet/scripts/create_agent_policies/create_agent_policies.ts new file mode 100644 index 00000000000000..83838b3443e905 --- /dev/null +++ b/x-pack/plugins/fleet/scripts/create_agent_policies/create_agent_policies.ts @@ -0,0 +1,134 @@ +/* + * 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 { ToolingLog } from '@kbn/tooling-log'; +import yargs from 'yargs'; + +import { LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE } from '../../common'; + +const logger = new ToolingLog({ + level: 'info', + writeTo: process.stdout, +}); + +const ES_URL = 'http://localhost:9200'; +const ES_SUPERUSER = 'system_indices_superuser'; +const ES_PASSWORD = 'changeme'; + +const printUsage = () => + logger.info(` + Create mocked agent policies to test Fleet UI at scale +`); + +const INDEX_BULK_OP = '{ "index":{ "_id": "{{id}}" } }\n'; + +async function createAgentPoliciesDocsBulk(size: number) { + const auth = 'Basic ' + Buffer.from(ES_SUPERUSER + ':' + ES_PASSWORD).toString('base64'); + const body = Array.from({ length: size }, (_, index) => index + 1) + .flatMap((idx) => [ + INDEX_BULK_OP.replace( + /{{id}}/, + `${LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE}:test-policy-${idx}` + ), + JSON.stringify({ + [LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE]: { + namespace: 'default', + monitoring_enabled: ['logs', 'metrics', 'traces'], + name: `Test Policy ${idx}`, + description: 'test policy', + is_default: false, + is_default_fleet_server: false, + inactivity_timeout: 1209600, + is_preconfigured: false, + status: 'active', + is_managed: false, + revision: 2, + updated_at: new Date().toISOString(), + updated_by: 'system', + schema_version: '1.1.1', + is_protected: false, + }, + type: LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE, + references: [], + managed: false, + coreMigrationVersion: '8.8.0', + typeMigrationVersion: '10.3.0', + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + }) + '\n', + ]) + .join(''); + const res = await fetch(`${ES_URL}/.kibana_ingest/_bulk`, { + method: 'post', + body, + headers: { + Authorization: auth, + 'Content-Type': 'application/x-ndjson', + }, + }); + const data = await res.json(); + + if (!data.items) { + logger.error('Error creating agent policies docs: ' + JSON.stringify(data)); + process.exit(1); + } + return data; +} + +async function createEnrollmentToken(size: number) { + const auth = 'Basic ' + Buffer.from(ES_SUPERUSER + ':' + ES_PASSWORD).toString('base64'); + const body = Array.from({ length: size }, (_, index) => index + 1) + .flatMap((idx) => [ + INDEX_BULK_OP.replace(/{{id}}/, `test-enrollment-token-${idx}`), + JSON.stringify({ + active: true, + api_key_id: 'faketest123', + api_key: 'test==', + name: `Test Policy ${idx}`, + policy_id: `${LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE}:test-policy-${idx}`, + namespaces: [], + created_at: new Date().toISOString(), + }) + '\n', + ]) + .join(''); + + const res = await fetch(`${ES_URL}/.fleet-enrollment-api-keys/_bulk`, { + method: 'post', + body, + headers: { + Authorization: auth, + 'Content-Type': 'application/x-ndjson', + }, + }); + const data = await res.json(); + + if (!data.items) { + logger.error('Error creating agent policies docs: ' + JSON.stringify(data)); + process.exit(1); + } + return data; +} + +export async function run() { + const { + size: sizeArg = 500, + // ignore yargs positional args, we only care about named args + _, + $0, + ...otherArgs + } = yargs(process.argv.slice(2)).argv; + if (Object.keys(otherArgs).length) { + logger.error(`Unknown arguments: ${Object.keys(otherArgs).join(', ')}`); + printUsage(); + process.exit(0); + } + + const size = Number(sizeArg).valueOf(); + logger.info(`Creating ${size} policies`); + await Promise.all([createAgentPoliciesDocsBulk(size), createEnrollmentToken(size)]); + logger.info(`Succesfuly created ${size} policies`); +} diff --git a/x-pack/plugins/fleet/scripts/create_agent_policies/index.js b/x-pack/plugins/fleet/scripts/create_agent_policies/index.js new file mode 100644 index 00000000000000..a51859ee684c6b --- /dev/null +++ b/x-pack/plugins/fleet/scripts/create_agent_policies/index.js @@ -0,0 +1,17 @@ +/* + * 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. + */ + +require('../../../../../src/setup_node_env'); +require('./create_agent_policies').run(); + +/* +Usage: + +cd x-pack/plugins/fleet +node scripts/create_agents/index.js + +*/