1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-23 07:19:41 +02:00

fix(errors): surface react docker errors to front end [EE-7053] (#11726)

Co-authored-by: testa113 <testa113>
This commit is contained in:
Ali 2024-05-13 15:34:00 +12:00 committed by GitHub
parent 55667a878a
commit 6b5a402962
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 107 additions and 142 deletions

View file

@ -1,5 +1,4 @@
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import { RawAxiosRequestHeaders } from 'axios';
import axios, { parseAxiosError } from '@/portainer/services/axios'; import axios, { parseAxiosError } from '@/portainer/services/axios';
import { import {
@ -38,6 +37,7 @@ import { PortainerResponse } from '../../types';
import { connectContainer } from '../../networks/queries/useConnectContainer'; import { connectContainer } from '../../networks/queries/useConnectContainer';
import { DockerContainer } from '../types'; import { DockerContainer } from '../types';
import { queryKeys } from '../queries/query-keys'; import { queryKeys } from '../queries/query-keys';
import { addNodeHeader } from '../../proxy/addNodeHeader';
import { CreateContainerRequest } from './types'; import { CreateContainerRequest } from './types';
import { Values } from './useInitialValues'; import { Values } from './useInitialValues';
@ -286,11 +286,7 @@ async function createContainer(
{ nodeName }: { nodeName?: string } = {} { nodeName }: { nodeName?: string } = {}
) { ) {
try { try {
const headers: RawAxiosRequestHeaders = {}; const headers = addNodeHeader(nodeName);
if (nodeName) {
headers['X-PortainerAgent-Target'] = nodeName;
}
const { data } = await axios.post< const { data } = await axios.post<
PortainerResponse<{ Id: string; Warnings: Array<string> }> PortainerResponse<{ Id: string; Warnings: Array<string> }>

View file

@ -1,10 +1,10 @@
import { RawAxiosRequestHeaders } from 'axios';
import { EnvironmentId } from '@/react/portainer/environments/types'; import { EnvironmentId } from '@/react/portainer/environments/types';
import PortainerError from '@/portainer/error'; import PortainerError from '@/portainer/error';
import axios, { parseAxiosError } from '@/portainer/services/axios'; import axios, { parseAxiosError } from '@/portainer/services/axios';
import { genericHandler } from '@/docker/rest/response/handlers'; import { genericHandler } from '@/docker/rest/response/handlers';
import { addNodeHeader } from '../proxy/addNodeHeader';
import { ContainerId } from './types'; import { ContainerId } from './types';
export async function startContainer( export async function startContainer(
@ -12,12 +12,7 @@ export async function startContainer(
id: ContainerId, id: ContainerId,
{ nodeName }: { nodeName?: string } = {} { nodeName }: { nodeName?: string } = {}
) { ) {
const headers: RawAxiosRequestHeaders = {}; const headers = addNodeHeader(nodeName);
if (nodeName) {
headers['X-PortainerAgent-Target'] = nodeName;
}
try { try {
await axios.post<void>( await axios.post<void>(
urlBuilder(environmentId, id, 'start'), urlBuilder(environmentId, id, 'start'),
@ -34,13 +29,12 @@ export async function stopContainer(
id: ContainerId, id: ContainerId,
{ nodeName }: { nodeName?: string } = {} { nodeName }: { nodeName?: string } = {}
) { ) {
const headers: RawAxiosRequestHeaders = {}; const headers = addNodeHeader(nodeName);
try {
if (nodeName) {
headers['X-PortainerAgent-Target'] = nodeName;
}
await axios.post<void>(urlBuilder(endpointId, id, 'stop'), {}, { headers }); await axios.post<void>(urlBuilder(endpointId, id, 'stop'), {}, { headers });
} catch (e) {
throw parseAxiosError(e, 'Failed stopping container');
}
} }
export async function recreateContainer( export async function recreateContainer(
@ -49,12 +43,8 @@ export async function recreateContainer(
pullImage: boolean, pullImage: boolean,
{ nodeName }: { nodeName?: string } = {} { nodeName }: { nodeName?: string } = {}
) { ) {
const headers: RawAxiosRequestHeaders = {}; const headers = addNodeHeader(nodeName);
try {
if (nodeName) {
headers['X-PortainerAgent-Target'] = nodeName;
}
await axios.post<void>( await axios.post<void>(
`/docker/${endpointId}/containers/${id}/recreate`, `/docker/${endpointId}/containers/${id}/recreate`,
{ {
@ -62,6 +52,9 @@ export async function recreateContainer(
}, },
{ headers } { headers }
); );
} catch (e) {
throw parseAxiosError(e, 'Failed recreating container');
}
} }
export async function restartContainer( export async function restartContainer(
@ -69,17 +62,16 @@ export async function restartContainer(
id: ContainerId, id: ContainerId,
{ nodeName }: { nodeName?: string } = {} { nodeName }: { nodeName?: string } = {}
) { ) {
const headers: RawAxiosRequestHeaders = {}; const headers = addNodeHeader(nodeName);
try {
if (nodeName) {
headers['X-PortainerAgent-Target'] = nodeName;
}
await axios.post<void>( await axios.post<void>(
urlBuilder(endpointId, id, 'restart'), urlBuilder(endpointId, id, 'restart'),
{}, {},
{ headers } { headers }
); );
} catch (e) {
throw parseAxiosError(e, 'Failed restarting container');
}
} }
export async function killContainer( export async function killContainer(
@ -87,13 +79,12 @@ export async function killContainer(
id: ContainerId, id: ContainerId,
{ nodeName }: { nodeName?: string } = {} { nodeName }: { nodeName?: string } = {}
) { ) {
const headers: RawAxiosRequestHeaders = {}; const headers = addNodeHeader(nodeName);
try {
if (nodeName) {
headers['X-PortainerAgent-Target'] = nodeName;
}
await axios.post<void>(urlBuilder(endpointId, id, 'kill'), {}, { headers }); await axios.post<void>(urlBuilder(endpointId, id, 'kill'), {}, { headers });
} catch (e) {
throw parseAxiosError(e, 'Failed killing container');
}
} }
export async function pauseContainer( export async function pauseContainer(
@ -101,13 +92,16 @@ export async function pauseContainer(
id: ContainerId, id: ContainerId,
{ nodeName }: { nodeName?: string } = {} { nodeName }: { nodeName?: string } = {}
) { ) {
const headers: RawAxiosRequestHeaders = {}; const headers = addNodeHeader(nodeName);
try {
if (nodeName) { await axios.post<void>(
headers['X-PortainerAgent-Target'] = nodeName; urlBuilder(endpointId, id, 'pause'),
{},
{ headers }
);
} catch (e) {
throw parseAxiosError(e, 'Failed pausing container');
} }
await axios.post<void>(urlBuilder(endpointId, id, 'pause'), {}, { headers });
} }
export async function resumeContainer( export async function resumeContainer(
@ -115,17 +109,16 @@ export async function resumeContainer(
id: ContainerId, id: ContainerId,
{ nodeName }: { nodeName?: string } = {} { nodeName }: { nodeName?: string } = {}
) { ) {
const headers: RawAxiosRequestHeaders = {}; const headers = addNodeHeader(nodeName);
try {
if (nodeName) {
headers['X-PortainerAgent-Target'] = nodeName;
}
await axios.post<void>( await axios.post<void>(
urlBuilder(endpointId, id, 'unpause'), urlBuilder(endpointId, id, 'unpause'),
{}, {},
{ headers } { headers }
); );
} catch (e) {
throw parseAxiosError(e, 'Failed resuming container');
}
} }
export async function renameContainer( export async function renameContainer(
@ -134,17 +127,20 @@ export async function renameContainer(
name: string, name: string,
{ nodeName }: { nodeName?: string } = {} { nodeName }: { nodeName?: string } = {}
) { ) {
const headers: RawAxiosRequestHeaders = {}; const headers = addNodeHeader(nodeName);
try {
if (nodeName) {
headers['X-PortainerAgent-Target'] = nodeName;
}
await axios.post<void>( await axios.post<void>(
urlBuilder(endpointId, id, 'rename'), urlBuilder(endpointId, id, 'rename'),
{}, {},
{ params: { name }, transformResponse: genericHandler, headers } {
params: { name },
transformResponse: genericHandler,
headers,
}
); );
} catch (e) {
throw parseAxiosError(e, 'Failed renaming container');
}
} }
export async function removeContainer( export async function removeContainer(
@ -155,13 +151,8 @@ export async function removeContainer(
removeVolumes, removeVolumes,
}: { removeVolumes?: boolean; nodeName?: string } = {} }: { removeVolumes?: boolean; nodeName?: string } = {}
) { ) {
const headers = addNodeHeader(nodeName);
try { try {
const headers: RawAxiosRequestHeaders = {};
if (nodeName) {
headers['X-PortainerAgent-Target'] = nodeName;
}
const { data } = await axios.delete<null | { message: string }>( const { data } = await axios.delete<null | { message: string }>(
urlBuilder(endpointId, containerId), urlBuilder(endpointId, containerId),
{ {
@ -175,7 +166,7 @@ export async function removeContainer(
throw new PortainerError(data.message); throw new PortainerError(data.message);
} }
} catch (e) { } catch (e) {
throw new PortainerError('Unable to remove container', e as Error); throw parseAxiosError(e, 'Failed removing container');
} }
} }

View file

@ -1,16 +1,14 @@
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { EnvironmentId } from '@/react/portainer/environments/types'; import { EnvironmentId } from '@/react/portainer/environments/types';
import axios, { import axios, { parseAxiosError } from '@/portainer/services/axios';
agentTargetHeader,
parseAxiosError,
} from '@/portainer/services/axios';
import { withGlobalError } from '@/react-tools/react-query'; import { withGlobalError } from '@/react-tools/react-query';
import { urlBuilder } from '../containers.service'; import { urlBuilder } from '../containers.service';
import { DockerContainerResponse } from '../types/response'; import { DockerContainerResponse } from '../types/response';
import { toListViewModel } from '../utils'; import { toListViewModel } from '../utils';
import { DockerContainer } from '../types'; import { DockerContainer } from '../types';
import { addNodeHeader } from '../../proxy/addNodeHeader';
import { Filters } from './types'; import { Filters } from './types';
import { queryKeys } from './query-keys'; import { queryKeys } from './query-keys';
@ -52,16 +50,13 @@ export async function getContainers(
environmentId: EnvironmentId, environmentId: EnvironmentId,
{ all = true, filters, nodeName }: UseContainers = {} { all = true, filters, nodeName }: UseContainers = {}
) { ) {
const headers = addNodeHeader(nodeName);
try { try {
const { data } = await axios.get<DockerContainerResponse[]>( const { data } = await axios.get<DockerContainerResponse[]>(
urlBuilder(environmentId, undefined, 'json'), urlBuilder(environmentId, undefined, 'json'),
{ {
params: { all, filters: filters && JSON.stringify(filters) }, params: { all, filters: filters && JSON.stringify(filters) },
headers: nodeName headers,
? {
[agentTargetHeader]: nodeName,
}
: undefined,
} }
); );
return data.map((c) => toListViewModel(c)); return data.map((c) => toListViewModel(c));

View file

@ -6,7 +6,7 @@ import { genericHandler } from '@/docker/rest/response/handlers';
import { ContainerId } from '../types'; import { ContainerId } from '../types';
import { urlBuilder } from '../containers.service'; import { urlBuilder } from '../containers.service';
import { addNodeName } from '../../proxy/addNodeName'; import { addNodeHeader } from '../../proxy/addNodeHeader';
import { queryKeys } from './query-keys'; import { queryKeys } from './query-keys';
import { ContainerJSON } from './container'; import { ContainerJSON } from './container';
@ -30,7 +30,7 @@ export async function inspectContainer(
try { try {
const { data } = await axios.get<ContainerJSON>( const { data } = await axios.get<ContainerJSON>(
urlBuilder(environmentId, id, 'json'), urlBuilder(environmentId, id, 'json'),
{ transformResponse: genericHandler, headers: addNodeName(nodeName) } { transformResponse: genericHandler, headers: addNodeHeader(nodeName) }
); );
return data; return data;
} catch (e) { } catch (e) {

View file

@ -1,10 +1,10 @@
import { Resources, RestartPolicy } from 'docker-types/generated/1.41'; import { Resources, RestartPolicy } from 'docker-types/generated/1.41';
import { RawAxiosRequestHeaders } from 'axios';
import axios, { parseAxiosError } from '@/portainer/services/axios'; import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types'; import { EnvironmentId } from '@/react/portainer/environments/types';
import { urlBuilder } from '../containers.service'; import { urlBuilder } from '../containers.service';
import { addNodeHeader } from '../../proxy/addNodeHeader';
/** /**
* UpdateConfig holds the mutable attributes of a Container. * UpdateConfig holds the mutable attributes of a Container.
@ -22,11 +22,7 @@ export async function updateContainer(
config: UpdateConfig, config: UpdateConfig,
{ nodeName }: { nodeName?: string } = {} { nodeName }: { nodeName?: string } = {}
) { ) {
const headers: RawAxiosRequestHeaders = {}; const headers = addNodeHeader(nodeName);
if (nodeName) {
headers['X-PortainerAgent-Target'] = nodeName;
}
try { try {
await axios.post<{ Warnings: string[] }>( await axios.post<{ Warnings: string[] }>(

View file

@ -51,6 +51,6 @@ async function getImages(
); );
return data; return data;
} catch (err) { } catch (err) {
throw parseAxiosError(err as Error, 'Unable to retrieve images'); throw parseAxiosError(err, 'Unable to retrieve images');
} }
} }

View file

@ -5,6 +5,7 @@ import { EnvironmentId } from '@/react/portainer/environments/types';
import { Registry } from '@/react/portainer/registries/types/registry'; import { Registry } from '@/react/portainer/registries/types/registry';
import { buildImageFullURI } from '../utils'; import { buildImageFullURI } from '../utils';
import { addNodeHeader } from '../../proxy/addNodeHeader';
import { encodeRegistryCredentials } from './encodeRegistryCredentials'; import { encodeRegistryCredentials } from './encodeRegistryCredentials';
import { buildProxyUrl } from './build-url'; import { buildProxyUrl } from './build-url';
@ -31,13 +32,11 @@ export async function pullImage({
const imageURI = buildImageFullURI(image, registry); const imageURI = buildImageFullURI(image, registry);
const headers: RawAxiosRequestHeaders = { const authHeaders: RawAxiosRequestHeaders = {
'X-Registry-Auth': authenticationDetails, 'X-Registry-Auth': authenticationDetails,
}; };
if (nodeName) { const headers = addNodeHeader(nodeName, authHeaders);
headers['X-PortainerAgent-Target'] = nodeName;
}
try { try {
await axios.post(buildProxyUrl(environmentId, { action: 'create' }), null, { await axios.post(buildProxyUrl(environmentId, { action: 'create' }), null, {

View file

@ -1,10 +1,9 @@
import { ContainerId } from '@/react/docker/containers/types'; import { ContainerId } from '@/react/docker/containers/types';
import axios, { import axios, { parseAxiosError } from '@/portainer/services/axios';
agentTargetHeader,
parseAxiosError,
} from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types'; import { EnvironmentId } from '@/react/portainer/environments/types';
import { addNodeHeader } from '../proxy/addNodeHeader';
import { NetworkId, DockerNetwork } from './types'; import { NetworkId, DockerNetwork } from './types';
type NetworkAction = 'connect' | 'disconnect' | 'create'; type NetworkAction = 'connect' | 'disconnect' | 'create';
@ -14,20 +13,15 @@ export async function getNetwork(
networkId: NetworkId, networkId: NetworkId,
{ nodeName }: { nodeName?: string } = {} { nodeName }: { nodeName?: string } = {}
) { ) {
const headers = addNodeHeader(nodeName);
try { try {
const { data: network } = await axios.get<DockerNetwork>( const { data: network } = await axios.get<DockerNetwork>(
buildUrl(environmentId, networkId), buildUrl(environmentId, networkId),
nodeName { headers }
? {
headers: {
[agentTargetHeader]: nodeName,
},
}
: undefined
); );
return network; return network;
} catch (e) { } 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)); await axios.delete(buildUrl(environmentId, networkId));
return networkId; return networkId;
} catch (e) { } 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 }; return { networkId, environmentId };
} catch (e) { } catch (e) {
throw parseAxiosError( throw parseAxiosError(e, 'Unable to disconnect container from network');
e as Error,
'Unable to disconnect container from network'
);
} }
} }

View file

@ -1,5 +1,4 @@
import { EndpointSettings } from 'docker-types/generated/1.41'; import { EndpointSettings } from 'docker-types/generated/1.41';
import { RawAxiosRequestHeaders } from 'axios';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios, { parseAxiosError } from '@/portainer/services/axios'; import axios, { parseAxiosError } from '@/portainer/services/axios';
@ -11,6 +10,7 @@ import {
} from '@/react-tools/react-query'; } from '@/react-tools/react-query';
import { queryKeys as dockerQueryKeys } from '../../queries/utils'; import { queryKeys as dockerQueryKeys } from '../../queries/utils';
import { addNodeHeader } from '../../proxy/addNodeHeader';
import { buildUrl } from './buildUrl'; import { buildUrl } from './buildUrl';
@ -56,18 +56,15 @@ export async function connectContainer({
}; };
} }
const headers: RawAxiosRequestHeaders = {}; const headers = addNodeHeader(nodeName);
if (nodeName) {
headers['X-PortainerAgent-Target'] = nodeName;
}
try { try {
await axios.post( await axios.post(
buildUrl(environmentId, { id: networkId, action: 'connect' }), buildUrl(environmentId, { id: networkId, action: 'connect' }),
payload payload,
{ headers }
); );
} catch (err) { } catch (err) {
throw parseAxiosError(err as Error, 'Unable to connect container'); throw parseAxiosError(err, 'Unable to connect container');
} }
} }

View file

@ -54,6 +54,6 @@ export async function getNetworks(
network.Attachable === true) network.Attachable === true)
); );
} catch (err) { } catch (err) {
throw parseAxiosError(err as Error, 'Unable to retrieve networks'); throw parseAxiosError(err, 'Unable to retrieve networks');
} }
} }

View file

@ -1,8 +1,8 @@
import { RawAxiosRequestHeaders } from 'axios'; import { RawAxiosRequestHeaders } from 'axios';
const AgentTargetHeader = 'X-PortainerAgent-Target'; import { agentTargetHeader } from '@/portainer/services/axios';
export function addNodeName( export function addNodeHeader(
nodeName?: string, nodeName?: string,
headers: RawAxiosRequestHeaders = {} headers: RawAxiosRequestHeaders = {}
) { ) {
@ -12,6 +12,6 @@ export function addNodeName(
return { return {
...headers, ...headers,
[AgentTargetHeader]: nodeName, [agentTargetHeader]: nodeName,
}; };
} }

View file

@ -31,6 +31,6 @@ async function getImages(environmentId: EnvironmentId) {
); );
return data; return data;
} catch (err) { } catch (err) {
throw parseAxiosError(err as Error, 'Unable to retrieve images'); throw parseAxiosError(err, 'Unable to retrieve images');
} }
} }

View file

@ -13,7 +13,7 @@ export async function getInfo(environmentId: EnvironmentId) {
); );
return data; return data;
} catch (err) { } catch (err) {
throw parseAxiosError(err as Error, 'Unable to retrieve version'); throw parseAxiosError(err, 'Unable to retrieve version');
} }
} }

View file

@ -20,7 +20,7 @@ export async function getPlugins(environmentId: EnvironmentId) {
); );
return data; return data;
} catch (e) { } catch (e) {
throw parseAxiosError(e as Error, 'Unable to retrieve plugins'); throw parseAxiosError(e, 'Unable to retrieve plugins');
} }
} }

View file

@ -13,7 +13,7 @@ export async function getVersion(environmentId: EnvironmentId) {
); );
return data; return data;
} catch (err) { } catch (err) {
throw parseAxiosError(err as Error, 'Unable to retrieve version'); throw parseAxiosError(err, 'Unable to retrieve version');
} }
} }

View file

@ -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'; import { ImageStatus } from '@/react/docker/components/ImageStatus/types';
export async function getStackImagesStatus(id: number) { export async function getStackImagesStatus(id: number) {
@ -8,9 +8,9 @@ export async function getStackImagesStatus(id: number) {
); );
return data; return data;
} catch (e) { } catch (e) {
return { throw parseAxiosError(
Status: 'unknown', e,
Message: `Unable to retrieve image status for stack: ${id}`, `Unable to retrieve image status for stack: ${id}`
}; );
} }
} }

View file

@ -28,6 +28,6 @@ export async function getVolumes(environmentId: EnvironmentId) {
return data.Volumes; return data.Volumes;
} catch (error) { } catch (error) {
throw parseAxiosError(error as Error, 'Unable to retrieve volumes'); throw parseAxiosError(error, 'Unable to retrieve volumes');
} }
} }