Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fleet] Stop fetching all full or with agentCount agent policies #191661

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
}),
Expand All @@ -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: [
Expand Down Expand Up @@ -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',
Expand All @@ -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]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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();
Expand Down Expand Up @@ -117,6 +169,9 @@ export function useFetchAgentsData() {
const [totalInactiveAgents, setTotalInactiveAgents] = useState(0);
const [totalManagedAgentIds, setTotalManagedAgentIds] = useState<string[]>([]);
const [managedAgentsOnCurrentPage, setManagedAgentsOnCurrentPage] = useState(0);
const [agentPoliciesIndexedById, setAgentPoliciesIndexedByIds] = useState<{
[k: string]: AgentPolicy;
}>({});

const [latestAgentActionErrors, setLatestAgentActionErrors] = useState<string[]>([]);

Expand Down Expand Up @@ -180,7 +235,6 @@ export function useFetchAgentsData() {
}),
]);

isLoadingVar.current = false;
// Return if a newer request has been triggered
if (currentRequestRef.current !== currentRequest) {
return;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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',
Expand All @@ -275,6 +349,7 @@ export function useFetchAgentsData() {
fetchDataAsync();
},
[
fullAgentPolicyFecher,
pagination.currentPage,
pagination.pageSize,
kuery,
Expand Down Expand Up @@ -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,
Expand All @@ -341,7 +408,7 @@ export function useFetchAgentsData() {
setSelectedStatus,
selectedTags,
setSelectedTags,
agentPolicies,
allAgentPolicies,
agentPoliciesRequest,
agentPoliciesIndexedById,
pagination,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
setSelectedStatus,
selectedTags,
setSelectedTags,
agentPolicies,
allAgentPolicies,
agentPoliciesRequest,
agentPoliciesIndexedById,
pagination,
Expand Down Expand Up @@ -286,14 +286,14 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
refreshAgentActivity={isLoading}
setSearch={setSearch}
setSelectedStatus={setSelectedStatus}
agentPolicies={agentPolicies}
agentPolicies={allAgentPolicies}
/>
</EuiPortal>
) : null}
{enrollmentFlyout.isOpen ? (
<EuiPortal>
<AgentEnrollmentFlyout
agentPolicy={agentPolicies.find((p) => p.id === enrollmentFlyout.selectedPolicyId)}
agentPolicy={allAgentPolicies.find((p) => p.id === enrollmentFlyout.selectedPolicyId)}
onClose={() => {
setEnrollmentFlyoutState({ isOpen: false });
fetchData();
Expand Down Expand Up @@ -401,7 +401,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
)}
{/* Search and filter bar */}
<SearchAndFilterBar
agentPolicies={agentPolicies}
agentPolicies={allAgentPolicies}
draftKuery={draftKuery}
onDraftKueryChange={setDraftKuery}
onSubmitSearch={onSubmitSearch}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
* 2.0.
*/

import { useAuthz, useGetAgentPoliciesQuery, useGetAgentsQuery } from '../../../../../hooks';
import { policyHasFleetServer } from '../../../../../services';
import {
FLEET_SERVER_PACKAGE,
LEGACY_PACKAGE_POLICY_SAVED_OBJECT_TYPE,
} from '../../../../../../../../common/constants';
import { useAuthz, useGetAgentsQuery, useGetPackagePoliciesQuery } from '../../../../../hooks';

interface UseIsFirstTimeAgentUserResponse {
isFirstTimeAgentUser?: boolean;
Expand All @@ -16,23 +19,22 @@ interface UseIsFirstTimeAgentUserResponse {
export const useIsFirstTimeAgentUserQuery = (): UseIsFirstTimeAgentUserResponse => {
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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 '..';

Expand All @@ -57,11 +57,6 @@ export const AgentEnrollmentFlyout: React.FunctionComponent<FlyOutProps> = ({
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();
Expand All @@ -87,7 +82,7 @@ export const AgentEnrollmentFlyout: React.FunctionComponent<FlyOutProps> = ({

const selectedPolicy = agentPolicyWithPackagePolicies
? agentPolicyWithPackagePolicies
: findPolicyById(agentPolicies, selectedPolicyId);
: undefined;

const hasNoFleetServerHost = fleetStatus.isReady && !fleetServerHost;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -22,7 +20,6 @@ interface AgentEnrollmentFlyoutData {
}

export function useAgentEnrollmentFlyoutData(): AgentEnrollmentFlyoutData {
const authz = useAuthz();
const {
data: agentPoliciesData,
isInitialRequest: isInitialAgentPolicyRequest,
Expand All @@ -31,7 +28,6 @@ export function useAgentEnrollmentFlyoutData(): AgentEnrollmentFlyoutData {
} = useGetAgentPolicies({
page: 1,
perPage: SO_SEARCH_LIMIT,
full: authz.fleet.readAgentPolicies,
});

const {
Expand Down
Loading
Loading