From 6b5a402962dc07f15f0af4e160636c580a74cc6a Mon Sep 17 00:00:00 2001 From: Ali <83188384+testA113@users.noreply.github.com> Date: Mon, 13 May 2024 15:34:00 +1200 Subject: [PATCH] fix(errors): surface react docker errors to front end [EE-7053] (#11726) Co-authored-by: testa113 --- .../CreateView/useCreateMutation.tsx | 8 +- .../docker/containers/containers.service.ts | 141 ++++++++---------- .../docker/containers/queries/containers.ts | 13 +- .../containers/queries/useContainerInspect.ts | 4 +- .../containers/queries/useUpdateContainer.ts | 8 +- app/react/docker/images/queries/useImages.ts | 2 +- .../images/queries/usePullImageMutation.ts | 7 +- app/react/docker/networks/network.service.ts | 25 +--- .../networks/queries/useConnectContainer.ts | 13 +- .../docker/networks/queries/useNetworks.ts | 2 +- .../{addNodeName.ts => addNodeHeader.ts} | 6 +- .../docker/proxy/queries/images/useImages.ts | 2 +- app/react/docker/proxy/queries/useInfo.ts | 2 +- .../docker/proxy/queries/useServicePlugins.ts | 2 +- app/react/docker/proxy/queries/useVersion.ts | 2 +- .../columns/getStackImagesStatus.ts | 10 +- .../docker/volumes/queries/useVolumes.ts | 2 +- 17 files changed, 107 insertions(+), 142 deletions(-) rename app/react/docker/proxy/{addNodeName.ts => addNodeHeader.ts} (59%) diff --git a/app/react/docker/containers/CreateView/useCreateMutation.tsx b/app/react/docker/containers/CreateView/useCreateMutation.tsx index 2c7bb167c..7d4228678 100644 --- a/app/react/docker/containers/CreateView/useCreateMutation.tsx +++ b/app/react/docker/containers/CreateView/useCreateMutation.tsx @@ -1,5 +1,4 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { RawAxiosRequestHeaders } from 'axios'; import axios, { parseAxiosError } from '@/portainer/services/axios'; import { @@ -38,6 +37,7 @@ import { PortainerResponse } from '../../types'; import { connectContainer } from '../../networks/queries/useConnectContainer'; import { DockerContainer } from '../types'; import { queryKeys } from '../queries/query-keys'; +import { addNodeHeader } from '../../proxy/addNodeHeader'; import { CreateContainerRequest } from './types'; import { Values } from './useInitialValues'; @@ -286,11 +286,7 @@ async function createContainer( { nodeName }: { nodeName?: string } = {} ) { try { - const headers: RawAxiosRequestHeaders = {}; - - if (nodeName) { - headers['X-PortainerAgent-Target'] = nodeName; - } + const headers = addNodeHeader(nodeName); const { data } = await axios.post< PortainerResponse<{ Id: string; Warnings: Array }> diff --git a/app/react/docker/containers/containers.service.ts b/app/react/docker/containers/containers.service.ts index f9e2e1cda..8bcda4d43 100644 --- a/app/react/docker/containers/containers.service.ts +++ b/app/react/docker/containers/containers.service.ts @@ -1,10 +1,10 @@ -import { RawAxiosRequestHeaders } from 'axios'; - import { EnvironmentId } from '@/react/portainer/environments/types'; import PortainerError from '@/portainer/error'; import axios, { parseAxiosError } from '@/portainer/services/axios'; import { genericHandler } from '@/docker/rest/response/handlers'; +import { addNodeHeader } from '../proxy/addNodeHeader'; + import { ContainerId } from './types'; export async function startContainer( @@ -12,12 +12,7 @@ export async function startContainer( id: ContainerId, { nodeName }: { nodeName?: string } = {} ) { - const headers: RawAxiosRequestHeaders = {}; - - if (nodeName) { - headers['X-PortainerAgent-Target'] = nodeName; - } - + const headers = addNodeHeader(nodeName); try { await axios.post( urlBuilder(environmentId, id, 'start'), @@ -34,13 +29,12 @@ export async function stopContainer( id: ContainerId, { nodeName }: { nodeName?: string } = {} ) { - const headers: RawAxiosRequestHeaders = {}; - - if (nodeName) { - headers['X-PortainerAgent-Target'] = nodeName; + const headers = addNodeHeader(nodeName); + try { + await axios.post(urlBuilder(endpointId, id, 'stop'), {}, { headers }); + } catch (e) { + throw parseAxiosError(e, 'Failed stopping container'); } - - await axios.post(urlBuilder(endpointId, id, 'stop'), {}, { headers }); } export async function recreateContainer( @@ -49,19 +43,18 @@ export async function recreateContainer( pullImage: boolean, { nodeName }: { nodeName?: string } = {} ) { - const headers: RawAxiosRequestHeaders = {}; - - if (nodeName) { - headers['X-PortainerAgent-Target'] = nodeName; + const headers = addNodeHeader(nodeName); + try { + await axios.post( + `/docker/${endpointId}/containers/${id}/recreate`, + { + PullImage: pullImage, + }, + { headers } + ); + } catch (e) { + throw parseAxiosError(e, 'Failed recreating container'); } - - await axios.post( - `/docker/${endpointId}/containers/${id}/recreate`, - { - PullImage: pullImage, - }, - { headers } - ); } export async function restartContainer( @@ -69,17 +62,16 @@ export async function restartContainer( id: ContainerId, { nodeName }: { nodeName?: string } = {} ) { - const headers: RawAxiosRequestHeaders = {}; - - if (nodeName) { - headers['X-PortainerAgent-Target'] = nodeName; + const headers = addNodeHeader(nodeName); + try { + await axios.post( + urlBuilder(endpointId, id, 'restart'), + {}, + { headers } + ); + } catch (e) { + throw parseAxiosError(e, 'Failed restarting container'); } - - await axios.post( - urlBuilder(endpointId, id, 'restart'), - {}, - { headers } - ); } export async function killContainer( @@ -87,13 +79,12 @@ export async function killContainer( id: ContainerId, { nodeName }: { nodeName?: string } = {} ) { - const headers: RawAxiosRequestHeaders = {}; - - if (nodeName) { - headers['X-PortainerAgent-Target'] = nodeName; + const headers = addNodeHeader(nodeName); + try { + await axios.post(urlBuilder(endpointId, id, 'kill'), {}, { headers }); + } catch (e) { + throw parseAxiosError(e, 'Failed killing container'); } - - await axios.post(urlBuilder(endpointId, id, 'kill'), {}, { headers }); } export async function pauseContainer( @@ -101,13 +92,16 @@ export async function pauseContainer( id: ContainerId, { nodeName }: { nodeName?: string } = {} ) { - const headers: RawAxiosRequestHeaders = {}; - - if (nodeName) { - headers['X-PortainerAgent-Target'] = nodeName; + const headers = addNodeHeader(nodeName); + try { + await axios.post( + urlBuilder(endpointId, id, 'pause'), + {}, + { headers } + ); + } catch (e) { + throw parseAxiosError(e, 'Failed pausing container'); } - - await axios.post(urlBuilder(endpointId, id, 'pause'), {}, { headers }); } export async function resumeContainer( @@ -115,17 +109,16 @@ export async function resumeContainer( id: ContainerId, { nodeName }: { nodeName?: string } = {} ) { - const headers: RawAxiosRequestHeaders = {}; - - if (nodeName) { - headers['X-PortainerAgent-Target'] = nodeName; + const headers = addNodeHeader(nodeName); + try { + await axios.post( + urlBuilder(endpointId, id, 'unpause'), + {}, + { headers } + ); + } catch (e) { + throw parseAxiosError(e, 'Failed resuming container'); } - - await axios.post( - urlBuilder(endpointId, id, 'unpause'), - {}, - { headers } - ); } export async function renameContainer( @@ -134,17 +127,20 @@ export async function renameContainer( name: string, { nodeName }: { nodeName?: string } = {} ) { - const headers: RawAxiosRequestHeaders = {}; - - if (nodeName) { - headers['X-PortainerAgent-Target'] = nodeName; + const headers = addNodeHeader(nodeName); + try { + await axios.post( + urlBuilder(endpointId, id, 'rename'), + {}, + { + params: { name }, + transformResponse: genericHandler, + headers, + } + ); + } catch (e) { + throw parseAxiosError(e, 'Failed renaming container'); } - - await axios.post( - urlBuilder(endpointId, id, 'rename'), - {}, - { params: { name }, transformResponse: genericHandler, headers } - ); } export async function removeContainer( @@ -155,13 +151,8 @@ export async function removeContainer( removeVolumes, }: { removeVolumes?: boolean; nodeName?: string } = {} ) { + const headers = addNodeHeader(nodeName); try { - const headers: RawAxiosRequestHeaders = {}; - - if (nodeName) { - headers['X-PortainerAgent-Target'] = nodeName; - } - const { data } = await axios.delete( urlBuilder(endpointId, containerId), { @@ -175,7 +166,7 @@ export async function removeContainer( throw new PortainerError(data.message); } } catch (e) { - throw new PortainerError('Unable to remove container', e as Error); + throw parseAxiosError(e, 'Failed removing container'); } } diff --git a/app/react/docker/containers/queries/containers.ts b/app/react/docker/containers/queries/containers.ts index 020b7749e..b723f8e54 100644 --- a/app/react/docker/containers/queries/containers.ts +++ b/app/react/docker/containers/queries/containers.ts @@ -1,16 +1,14 @@ import { useQuery } from '@tanstack/react-query'; import { EnvironmentId } from '@/react/portainer/environments/types'; -import axios, { - agentTargetHeader, - parseAxiosError, -} from '@/portainer/services/axios'; +import axios, { parseAxiosError } from '@/portainer/services/axios'; import { withGlobalError } from '@/react-tools/react-query'; import { urlBuilder } from '../containers.service'; import { DockerContainerResponse } from '../types/response'; import { toListViewModel } from '../utils'; import { DockerContainer } from '../types'; +import { addNodeHeader } from '../../proxy/addNodeHeader'; import { Filters } from './types'; import { queryKeys } from './query-keys'; @@ -52,16 +50,13 @@ export async function getContainers( environmentId: EnvironmentId, { all = true, filters, nodeName }: UseContainers = {} ) { + const headers = addNodeHeader(nodeName); try { const { data } = await axios.get( urlBuilder(environmentId, undefined, 'json'), { params: { all, filters: filters && JSON.stringify(filters) }, - headers: nodeName - ? { - [agentTargetHeader]: nodeName, - } - : undefined, + headers, } ); return data.map((c) => toListViewModel(c)); diff --git a/app/react/docker/containers/queries/useContainerInspect.ts b/app/react/docker/containers/queries/useContainerInspect.ts index d8f7a9ffc..a02accd98 100644 --- a/app/react/docker/containers/queries/useContainerInspect.ts +++ b/app/react/docker/containers/queries/useContainerInspect.ts @@ -6,7 +6,7 @@ import { genericHandler } from '@/docker/rest/response/handlers'; import { ContainerId } from '../types'; import { urlBuilder } from '../containers.service'; -import { addNodeName } from '../../proxy/addNodeName'; +import { addNodeHeader } from '../../proxy/addNodeHeader'; import { queryKeys } from './query-keys'; import { ContainerJSON } from './container'; @@ -30,7 +30,7 @@ export async function inspectContainer( try { const { data } = await axios.get( urlBuilder(environmentId, id, 'json'), - { transformResponse: genericHandler, headers: addNodeName(nodeName) } + { transformResponse: genericHandler, headers: addNodeHeader(nodeName) } ); return data; } catch (e) { diff --git a/app/react/docker/containers/queries/useUpdateContainer.ts b/app/react/docker/containers/queries/useUpdateContainer.ts index 2d23cd361..21e2de95f 100644 --- a/app/react/docker/containers/queries/useUpdateContainer.ts +++ b/app/react/docker/containers/queries/useUpdateContainer.ts @@ -1,10 +1,10 @@ import { Resources, RestartPolicy } from 'docker-types/generated/1.41'; -import { RawAxiosRequestHeaders } from 'axios'; import axios, { parseAxiosError } from '@/portainer/services/axios'; import { EnvironmentId } from '@/react/portainer/environments/types'; import { urlBuilder } from '../containers.service'; +import { addNodeHeader } from '../../proxy/addNodeHeader'; /** * UpdateConfig holds the mutable attributes of a Container. @@ -22,11 +22,7 @@ export async function updateContainer( config: UpdateConfig, { nodeName }: { nodeName?: string } = {} ) { - const headers: RawAxiosRequestHeaders = {}; - - if (nodeName) { - headers['X-PortainerAgent-Target'] = nodeName; - } + const headers = addNodeHeader(nodeName); try { await axios.post<{ Warnings: string[] }>( diff --git a/app/react/docker/images/queries/useImages.ts b/app/react/docker/images/queries/useImages.ts index a1f01f9bf..06dae535e 100644 --- a/app/react/docker/images/queries/useImages.ts +++ b/app/react/docker/images/queries/useImages.ts @@ -51,6 +51,6 @@ async function getImages( ); return data; } catch (err) { - throw parseAxiosError(err as Error, 'Unable to retrieve images'); + throw parseAxiosError(err, 'Unable to retrieve images'); } } diff --git a/app/react/docker/images/queries/usePullImageMutation.ts b/app/react/docker/images/queries/usePullImageMutation.ts index e1d00fd01..0ec8c099e 100644 --- a/app/react/docker/images/queries/usePullImageMutation.ts +++ b/app/react/docker/images/queries/usePullImageMutation.ts @@ -5,6 +5,7 @@ import { EnvironmentId } from '@/react/portainer/environments/types'; import { Registry } from '@/react/portainer/registries/types/registry'; import { buildImageFullURI } from '../utils'; +import { addNodeHeader } from '../../proxy/addNodeHeader'; import { encodeRegistryCredentials } from './encodeRegistryCredentials'; import { buildProxyUrl } from './build-url'; @@ -31,13 +32,11 @@ export async function pullImage({ const imageURI = buildImageFullURI(image, registry); - const headers: RawAxiosRequestHeaders = { + const authHeaders: RawAxiosRequestHeaders = { 'X-Registry-Auth': authenticationDetails, }; - if (nodeName) { - headers['X-PortainerAgent-Target'] = nodeName; - } + const headers = addNodeHeader(nodeName, authHeaders); try { await axios.post(buildProxyUrl(environmentId, { action: 'create' }), null, { diff --git a/app/react/docker/networks/network.service.ts b/app/react/docker/networks/network.service.ts index 2aa4ca9f1..9a14a3062 100644 --- a/app/react/docker/networks/network.service.ts +++ b/app/react/docker/networks/network.service.ts @@ -1,10 +1,9 @@ import { ContainerId } from '@/react/docker/containers/types'; -import axios, { - agentTargetHeader, - parseAxiosError, -} from '@/portainer/services/axios'; +import axios, { parseAxiosError } from '@/portainer/services/axios'; import { EnvironmentId } from '@/react/portainer/environments/types'; +import { addNodeHeader } from '../proxy/addNodeHeader'; + import { NetworkId, DockerNetwork } from './types'; type NetworkAction = 'connect' | 'disconnect' | 'create'; @@ -14,20 +13,15 @@ export async function getNetwork( networkId: NetworkId, { nodeName }: { nodeName?: string } = {} ) { + const headers = addNodeHeader(nodeName); try { const { data: network } = await axios.get( buildUrl(environmentId, networkId), - nodeName - ? { - headers: { - [agentTargetHeader]: nodeName, - }, - } - : undefined + { headers } ); return network; } catch (e) { - throw parseAxiosError(e as Error, 'Unable to retrieve network details'); + throw parseAxiosError(e, 'Unable to retrieve network details'); } } @@ -39,7 +33,7 @@ export async function deleteNetwork( await axios.delete(buildUrl(environmentId, networkId)); return networkId; } catch (e) { - throw parseAxiosError(e as Error, 'Unable to remove network'); + throw parseAxiosError(e, 'Unable to remove network'); } } @@ -55,10 +49,7 @@ export async function disconnectContainer( }); return { networkId, environmentId }; } catch (e) { - throw parseAxiosError( - e as Error, - 'Unable to disconnect container from network' - ); + throw parseAxiosError(e, 'Unable to disconnect container from network'); } } diff --git a/app/react/docker/networks/queries/useConnectContainer.ts b/app/react/docker/networks/queries/useConnectContainer.ts index ad68bc90e..883e3bbf1 100644 --- a/app/react/docker/networks/queries/useConnectContainer.ts +++ b/app/react/docker/networks/queries/useConnectContainer.ts @@ -1,5 +1,4 @@ import { EndpointSettings } from 'docker-types/generated/1.41'; -import { RawAxiosRequestHeaders } from 'axios'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import axios, { parseAxiosError } from '@/portainer/services/axios'; @@ -11,6 +10,7 @@ import { } from '@/react-tools/react-query'; import { queryKeys as dockerQueryKeys } from '../../queries/utils'; +import { addNodeHeader } from '../../proxy/addNodeHeader'; import { buildUrl } from './buildUrl'; @@ -56,18 +56,15 @@ export async function connectContainer({ }; } - const headers: RawAxiosRequestHeaders = {}; - - if (nodeName) { - headers['X-PortainerAgent-Target'] = nodeName; - } + const headers = addNodeHeader(nodeName); try { await axios.post( buildUrl(environmentId, { id: networkId, action: 'connect' }), - payload + payload, + { headers } ); } catch (err) { - throw parseAxiosError(err as Error, 'Unable to connect container'); + throw parseAxiosError(err, 'Unable to connect container'); } } diff --git a/app/react/docker/networks/queries/useNetworks.ts b/app/react/docker/networks/queries/useNetworks.ts index b0c3483d4..f3564fef5 100644 --- a/app/react/docker/networks/queries/useNetworks.ts +++ b/app/react/docker/networks/queries/useNetworks.ts @@ -54,6 +54,6 @@ export async function getNetworks( network.Attachable === true) ); } catch (err) { - throw parseAxiosError(err as Error, 'Unable to retrieve networks'); + throw parseAxiosError(err, 'Unable to retrieve networks'); } } diff --git a/app/react/docker/proxy/addNodeName.ts b/app/react/docker/proxy/addNodeHeader.ts similarity index 59% rename from app/react/docker/proxy/addNodeName.ts rename to app/react/docker/proxy/addNodeHeader.ts index 79faa933b..460e18aa5 100644 --- a/app/react/docker/proxy/addNodeName.ts +++ b/app/react/docker/proxy/addNodeHeader.ts @@ -1,8 +1,8 @@ import { RawAxiosRequestHeaders } from 'axios'; -const AgentTargetHeader = 'X-PortainerAgent-Target'; +import { agentTargetHeader } from '@/portainer/services/axios'; -export function addNodeName( +export function addNodeHeader( nodeName?: string, headers: RawAxiosRequestHeaders = {} ) { @@ -12,6 +12,6 @@ export function addNodeName( return { ...headers, - [AgentTargetHeader]: nodeName, + [agentTargetHeader]: nodeName, }; } diff --git a/app/react/docker/proxy/queries/images/useImages.ts b/app/react/docker/proxy/queries/images/useImages.ts index 711d9d562..54fb901a9 100644 --- a/app/react/docker/proxy/queries/images/useImages.ts +++ b/app/react/docker/proxy/queries/images/useImages.ts @@ -31,6 +31,6 @@ async function getImages(environmentId: EnvironmentId) { ); return data; } catch (err) { - throw parseAxiosError(err as Error, 'Unable to retrieve images'); + throw parseAxiosError(err, 'Unable to retrieve images'); } } diff --git a/app/react/docker/proxy/queries/useInfo.ts b/app/react/docker/proxy/queries/useInfo.ts index 770a4154c..2e97c976a 100644 --- a/app/react/docker/proxy/queries/useInfo.ts +++ b/app/react/docker/proxy/queries/useInfo.ts @@ -13,7 +13,7 @@ export async function getInfo(environmentId: EnvironmentId) { ); return data; } catch (err) { - throw parseAxiosError(err as Error, 'Unable to retrieve version'); + throw parseAxiosError(err, 'Unable to retrieve version'); } } diff --git a/app/react/docker/proxy/queries/useServicePlugins.ts b/app/react/docker/proxy/queries/useServicePlugins.ts index 2b74440c7..ad4cdc761 100644 --- a/app/react/docker/proxy/queries/useServicePlugins.ts +++ b/app/react/docker/proxy/queries/useServicePlugins.ts @@ -20,7 +20,7 @@ export async function getPlugins(environmentId: EnvironmentId) { ); return data; } catch (e) { - throw parseAxiosError(e as Error, 'Unable to retrieve plugins'); + throw parseAxiosError(e, 'Unable to retrieve plugins'); } } diff --git a/app/react/docker/proxy/queries/useVersion.ts b/app/react/docker/proxy/queries/useVersion.ts index ded79282c..78f7f5418 100644 --- a/app/react/docker/proxy/queries/useVersion.ts +++ b/app/react/docker/proxy/queries/useVersion.ts @@ -13,7 +13,7 @@ export async function getVersion(environmentId: EnvironmentId) { ); return data; } catch (err) { - throw parseAxiosError(err as Error, 'Unable to retrieve version'); + throw parseAxiosError(err, 'Unable to retrieve version'); } } diff --git a/app/react/docker/stacks/ListView/StacksDatatable/columns/getStackImagesStatus.ts b/app/react/docker/stacks/ListView/StacksDatatable/columns/getStackImagesStatus.ts index 2d0d7c0c7..632fa0388 100644 --- a/app/react/docker/stacks/ListView/StacksDatatable/columns/getStackImagesStatus.ts +++ b/app/react/docker/stacks/ListView/StacksDatatable/columns/getStackImagesStatus.ts @@ -1,4 +1,4 @@ -import axios from '@/portainer/services/axios'; +import axios, { parseAxiosError } from '@/portainer/services/axios'; import { ImageStatus } from '@/react/docker/components/ImageStatus/types'; export async function getStackImagesStatus(id: number) { @@ -8,9 +8,9 @@ export async function getStackImagesStatus(id: number) { ); return data; } catch (e) { - return { - Status: 'unknown', - Message: `Unable to retrieve image status for stack: ${id}`, - }; + throw parseAxiosError( + e, + `Unable to retrieve image status for stack: ${id}` + ); } } diff --git a/app/react/docker/volumes/queries/useVolumes.ts b/app/react/docker/volumes/queries/useVolumes.ts index 35b09410f..0bd59ad57 100644 --- a/app/react/docker/volumes/queries/useVolumes.ts +++ b/app/react/docker/volumes/queries/useVolumes.ts @@ -28,6 +28,6 @@ export async function getVolumes(environmentId: EnvironmentId) { return data.Volumes; } catch (error) { - throw parseAxiosError(error as Error, 'Unable to retrieve volumes'); + throw parseAxiosError(error, 'Unable to retrieve volumes'); } }