From a61c1004d31be34665470e6aebbbc63f9b619551 Mon Sep 17 00:00:00 2001 From: Viktor Pettersson Date: Thu, 20 Mar 2025 10:05:15 +0100 Subject: [PATCH] fix(agent-updates): fix remote agent updates cannot be scheduled properly for large edge groups [BE-11691] (#528) --- .../environments/environment.service/index.ts | 2 ++ app/react/portainer/environments/types.ts | 2 ++ .../common/RollbackOptions.tsx | 24 +++++-------- .../common/ScheduleTypeSelector.tsx | 6 +--- .../common/useEdgeGroupsEnvironmentIds.ts | 34 ------------------ .../common/useEnvironments.ts | 8 ++--- .../update-schedules/queries/query-keys.ts | 8 +++-- .../queries/usePreviousVersions.ts | 36 +++++++++++-------- 8 files changed, 46 insertions(+), 74 deletions(-) delete mode 100644 app/react/portainer/environments/update-schedules/common/useEdgeGroupsEnvironmentIds.ts diff --git a/app/react/portainer/environments/environment.service/index.ts b/app/react/portainer/environments/environment.service/index.ts index 8971a4af0..f96d57d69 100644 --- a/app/react/portainer/environments/environment.service/index.ts +++ b/app/react/portainer/environments/environment.service/index.ts @@ -7,6 +7,7 @@ import { EnvironmentStatus, EnvironmentGroupId, PlatformType, + EdgeGroupId, } from '@/react/portainer/environments/types'; import { type TagId } from '@/portainer/tags/types'; import { UserId } from '@/portainer/users/types'; @@ -47,6 +48,7 @@ export interface BaseEnvironmentsQueryParams { updateInformation?: boolean; edgeCheckInPassedSeconds?: number; platformTypes?: PlatformType[]; + edgeGroupIds?: EdgeGroupId[]; } export type EnvironmentsQueryParams = BaseEnvironmentsQueryParams & diff --git a/app/react/portainer/environments/types.ts b/app/react/portainer/environments/types.ts index e9dc5aa25..9279e0d4c 100644 --- a/app/react/portainer/environments/types.ts +++ b/app/react/portainer/environments/types.ts @@ -3,6 +3,8 @@ import { DockerSnapshot } from '@/react/docker/snapshots/types'; export type EnvironmentGroupId = number; +export type EdgeGroupId = number; + type RoleId = number; interface AccessPolicy { RoleId: RoleId; diff --git a/app/react/portainer/environments/update-schedules/common/RollbackOptions.tsx b/app/react/portainer/environments/update-schedules/common/RollbackOptions.tsx index 4b4265a65..be354152c 100644 --- a/app/react/portainer/environments/update-schedules/common/RollbackOptions.tsx +++ b/app/react/portainer/environments/update-schedules/common/RollbackOptions.tsx @@ -9,7 +9,6 @@ import { TextTip } from '@@/Tip/TextTip'; import { usePreviousVersions } from '../queries/usePreviousVersions'; import { FormValues } from './types'; -import { useEdgeGroupsEnvironmentIds } from './useEdgeGroupsEnvironmentIds'; export function RollbackOptions() { const { isLoading, count, version, versionError } = useSelectVersionOnMount(); @@ -50,24 +49,19 @@ function useSelectVersionOnMount() { errors: { version: versionError }, } = useFormikContext(); - const environmentIdsQuery = useEdgeGroupsEnvironmentIds(groupIds); - - const previousVersionsQuery = usePreviousVersions({ - enabled: !!environmentIdsQuery.data, - }); + const previousVersionsQuery = usePreviousVersions(groupIds ?? []); const previousVersions = useMemo( () => previousVersionsQuery.data - ? _.uniq( - _.compact( - environmentIdsQuery.data?.map( - (envId) => previousVersionsQuery.data[envId] - ) - ) - ) + ? _.uniq(Object.values(previousVersionsQuery.data)) : [], - [environmentIdsQuery.data, previousVersionsQuery.data] + [previousVersionsQuery.data] + ); + + const previousVersionCount = useMemo( + () => Object.keys(previousVersions).length, + [previousVersions] ); useEffect(() => { @@ -91,7 +85,7 @@ function useSelectVersionOnMount() { isLoading: previousVersionsQuery.isLoading, versionError, version, - count: environmentIdsQuery.data?.length, + count: previousVersionCount, }; } diff --git a/app/react/portainer/environments/update-schedules/common/ScheduleTypeSelector.tsx b/app/react/portainer/environments/update-schedules/common/ScheduleTypeSelector.tsx index 212ff7c00..8d088f7d2 100644 --- a/app/react/portainer/environments/update-schedules/common/ScheduleTypeSelector.tsx +++ b/app/react/portainer/environments/update-schedules/common/ScheduleTypeSelector.tsx @@ -7,7 +7,6 @@ import { NavContainer } from '@@/NavTabs/NavContainer'; import { ScheduleType } from '../types'; -import { useEdgeGroupsEnvironmentIds } from './useEdgeGroupsEnvironmentIds'; import { useEnvironments } from './useEnvironments'; import { defaultValue } from './ScheduledTimeField'; import { FormValues } from './types'; @@ -17,10 +16,7 @@ import { RollbackScheduleDetailsFieldset } from './RollbackScheduleDetailsFields export function ScheduleTypeSelector() { const { values, setFieldValue } = useFormikContext(); - const environmentIdsQuery = useEdgeGroupsEnvironmentIds(values.groupIds); - - const edgeGroupsEnvironmentIds = environmentIdsQuery.data || []; - const environments = useEnvironments(edgeGroupsEnvironmentIds); + const environments = useEnvironments(values.groupIds); // old version is version that doesn't support scheduling of updates const hasNoTimeZone = environments.some((env) => !env.LocalTimeZone); diff --git a/app/react/portainer/environments/update-schedules/common/useEdgeGroupsEnvironmentIds.ts b/app/react/portainer/environments/update-schedules/common/useEdgeGroupsEnvironmentIds.ts deleted file mode 100644 index 6f44c7dab..000000000 --- a/app/react/portainer/environments/update-schedules/common/useEdgeGroupsEnvironmentIds.ts +++ /dev/null @@ -1,34 +0,0 @@ -import _ from 'lodash'; -import { useMemo } from 'react'; - -import { useEdgeGroups } from '@/react/edge/edge-groups/queries/useEdgeGroups'; -import { EdgeGroup } from '@/react/edge/edge-groups/types'; - -export function useEdgeGroupsEnvironmentIds( - edgeGroupsIds: Array -) { - const groupsQuery = useEdgeGroups({ - select: (groups) => - Object.fromEntries(groups.map((g) => [g.Id, g.Endpoints])), - }); - - const envIds = useMemo( - () => - _.uniq( - _.compact( - edgeGroupsIds.flatMap((id) => - groupsQuery.data ? groupsQuery.data[id] : [] - ) - ) - ), - [edgeGroupsIds, groupsQuery.data] - ); - - return useMemo( - () => ({ - data: groupsQuery.data ? envIds : null, - isLoading: groupsQuery.isLoading, - }), - [envIds, groupsQuery.data, groupsQuery.isLoading] - ); -} diff --git a/app/react/portainer/environments/update-schedules/common/useEnvironments.ts b/app/react/portainer/environments/update-schedules/common/useEnvironments.ts index 94ef89d86..bfe9a83b4 100644 --- a/app/react/portainer/environments/update-schedules/common/useEnvironments.ts +++ b/app/react/portainer/environments/update-schedules/common/useEnvironments.ts @@ -1,11 +1,11 @@ import { useEnvironmentList } from '@/react/portainer/environments/queries/useEnvironmentList'; -import { EdgeTypes, EnvironmentId } from '@/react/portainer/environments/types'; +import { EdgeGroupId, EdgeTypes } from '@/react/portainer/environments/types'; -export function useEnvironments(environmentsIds: Array) { +export function useEnvironments(edgeGroupIds: Array) { const environmentsQuery = useEnvironmentList( - { endpointIds: environmentsIds, types: EdgeTypes, pageLimit: 0 }, + { edgeGroupIds, types: EdgeTypes, pageLimit: 0 }, { - enabled: environmentsIds.length > 0, + enabled: edgeGroupIds.length > 0, } ); diff --git a/app/react/portainer/environments/update-schedules/queries/query-keys.ts b/app/react/portainer/environments/update-schedules/queries/query-keys.ts index ebb0b5112..f6e51532e 100644 --- a/app/react/portainer/environments/update-schedules/queries/query-keys.ts +++ b/app/react/portainer/environments/update-schedules/queries/query-keys.ts @@ -1,4 +1,7 @@ -import { EnvironmentId } from '@/react/portainer/environments/types'; +import { + EdgeGroupId, + EnvironmentId, +} from '@/react/portainer/environments/types'; import { EdgeUpdateSchedule } from '../types'; @@ -11,5 +14,6 @@ export const queryKeys = { [...queryKeys.base(), 'active', { environmentIds }] as const, supportedAgentVersions: () => [...queryKeys.base(), 'agent_versions'] as const, - previousVersions: () => [...queryKeys.base(), 'previous_versions'] as const, + previousVersions: (edgeGroupIds?: EdgeGroupId[]) => + [...queryKeys.base(), 'previous_versions', { edgeGroupIds }] as const, }; diff --git a/app/react/portainer/environments/update-schedules/queries/usePreviousVersions.ts b/app/react/portainer/environments/update-schedules/queries/usePreviousVersions.ts index be055ffdc..3f2905b2a 100644 --- a/app/react/portainer/environments/update-schedules/queries/usePreviousVersions.ts +++ b/app/react/portainer/environments/update-schedules/queries/usePreviousVersions.ts @@ -1,7 +1,10 @@ import { useQuery } from '@tanstack/react-query'; import axios, { parseAxiosError } from '@/portainer/services/axios'; -import { EnvironmentId } from '@/react/portainer/environments/types'; +import { + EdgeGroupId, + EnvironmentId, +} from '@/react/portainer/environments/types'; import { queryKeys } from './query-keys'; import { buildUrl } from './urls'; @@ -12,22 +15,27 @@ interface Options { enabled?: boolean; } -export function usePreviousVersions>({ - select, - onSuccess, - enabled, -}: Options = {}) { - return useQuery(queryKeys.previousVersions(), getPreviousVersions, { - select, - onSuccess, - enabled, - }); +export function usePreviousVersions>( + edgeGroupIds: EdgeGroupId[], + { select, enabled }: Options = {} +) { + return useQuery( + queryKeys.previousVersions(edgeGroupIds), + () => getPreviousVersions(edgeGroupIds), + { + select, + enabled: enabled && edgeGroupIds.length > 0, + } + ); } -async function getPreviousVersions() { +async function getPreviousVersions(edgeGroupIds: EdgeGroupId[]) { try { - const { data } = await axios.get>( - buildUrl(undefined, 'previous_versions') + const { data } = await axios.get>( + buildUrl(undefined, 'previous_versions'), + { + params: { edgeGroupIds }, + } ); return data; } catch (err) {