mirror of
https://github.com/portainer/portainer.git
synced 2025-07-24 07:49:41 +02:00
feat(app): limit the docker API version supported by the frontend (#12295)
Some checks failed
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Has been cancelled
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Has been cancelled
ci / build_images (map[arch:arm platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Has been cancelled
/ triage (push) Has been cancelled
Lint / Run linters (push) Has been cancelled
Test / test-client (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:linux]) (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Has been cancelled
Test / test-server (map[arch:arm64 platform:linux]) (push) Has been cancelled
ci / build_manifests (push) Has been cancelled
Some checks failed
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Has been cancelled
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Has been cancelled
ci / build_images (map[arch:arm platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Has been cancelled
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Has been cancelled
/ triage (push) Has been cancelled
Lint / Run linters (push) Has been cancelled
Test / test-client (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:linux]) (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Has been cancelled
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Has been cancelled
Test / test-server (map[arch:arm64 platform:linux]) (push) Has been cancelled
ci / build_manifests (push) Has been cancelled
This commit is contained in:
parent
8cbd23c059
commit
ac5491e864
227 changed files with 4702 additions and 3411 deletions
|
@ -1,15 +0,0 @@
|
|||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
export function buildUrl(
|
||||
environmentId: EnvironmentId,
|
||||
action: string,
|
||||
subAction = ''
|
||||
) {
|
||||
let url = `/endpoints/${environmentId}/docker/${action}`;
|
||||
|
||||
if (subAction) {
|
||||
url += `/${subAction}`;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
50
app/react/docker/proxy/queries/buildDockerProxyUrl.ts
Normal file
50
app/react/docker/proxy/queries/buildDockerProxyUrl.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { compact } from 'lodash';
|
||||
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
/**
|
||||
* Build docker proxy URL for Environment
|
||||
*
|
||||
* @param environmentId
|
||||
* @param action
|
||||
* @param subSegments Sub segments are only added to the URL when they are not `undefined`.
|
||||
* @returns `/endpoints/{environmentId}/docker/{action}/{subSegments[0]}/{subSegments[1]}/...`
|
||||
*
|
||||
* @example
|
||||
* // all calls return /endpoints/1/docker/action/sub1/sub2
|
||||
* buildDockerProxyUrl(1, 'action', 'sub1', 'sub2');
|
||||
* buildDockerProxyUrl(1, 'action', undefined, 'sub1', undefined, 'sub2');
|
||||
*
|
||||
* @example
|
||||
* function buildUrl(endpointId: EnvironmentId, id?: ServiceId, action?: string) {
|
||||
* return buildDockerProxyUrl(endpointId, 'services', id, action);
|
||||
*}
|
||||
*
|
||||
* // returns /endpoints/1/docker/services/ubx3r/update
|
||||
* buildUrl(1, 'ubx3r', 'update')
|
||||
*
|
||||
* // returns /endpoints/1/docker/services/update
|
||||
* buildUrl(1, undefined, 'update')
|
||||
*
|
||||
* // returns /endpoints/1/docker/services/ubx3r
|
||||
* buildUrl(1, 'ubx3r') // = buildUrl(1, 'ubx3r', undefined)
|
||||
*
|
||||
* // returns /endpoints/1/docker/services
|
||||
* buildUrl(1) // = buildUrl(1, undefined, undefined)
|
||||
*
|
||||
*/
|
||||
export function buildDockerProxyUrl(
|
||||
environmentId: EnvironmentId,
|
||||
action: string,
|
||||
...subSegments: unknown[]
|
||||
) {
|
||||
let url = `/endpoints/${environmentId}/docker/${action}`;
|
||||
|
||||
const joined = compact(subSegments).join('/');
|
||||
|
||||
if (joined) {
|
||||
url += `/${joined}`;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
31
app/react/docker/proxy/queries/images/useDownloadImages.ts
Normal file
31
app/react/docker/proxy/queries/images/useDownloadImages.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
import { formatArrayQueryParamsForDockerAPI } from '../utils';
|
||||
|
||||
/**
|
||||
* Raw docker API proxy
|
||||
*/
|
||||
export async function downloadImages(
|
||||
environmentId: EnvironmentId,
|
||||
images: { tags: string[]; id: string }[]
|
||||
) {
|
||||
const names = images.map((image) =>
|
||||
image.tags[0] !== '<none>:<none>' ? image.tags[0] : image.id
|
||||
);
|
||||
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
buildDockerProxyUrl(environmentId, 'images', 'get'),
|
||||
{
|
||||
params: { names },
|
||||
responseType: 'blob',
|
||||
paramsSerializer: formatArrayQueryParamsForDockerAPI,
|
||||
}
|
||||
);
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e, 'Unable to download images');
|
||||
}
|
||||
}
|
26
app/react/docker/proxy/queries/images/useImage.ts
Normal file
26
app/react/docker/proxy/queries/images/useImage.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { ImageInspect } from 'docker-types/generated/1.41';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
|
||||
/**
|
||||
* Raw docker API proxy
|
||||
* @param environmentId
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
export async function getImage(
|
||||
environmentId: EnvironmentId,
|
||||
id: Required<ImageInspect['Id']>
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<ImageInspect>(
|
||||
buildDockerProxyUrl(environmentId, 'images', id, 'json')
|
||||
);
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e, 'Unable to retrieve image');
|
||||
}
|
||||
}
|
32
app/react/docker/proxy/queries/images/useImageHistory.ts
Normal file
32
app/react/docker/proxy/queries/images/useImageHistory.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
|
||||
export type ImageLayer = {
|
||||
Id: string;
|
||||
Created: number;
|
||||
CreatedBy: string;
|
||||
Tags: string[];
|
||||
Size: number;
|
||||
Comment: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Raw docker API proxy
|
||||
* @param environmentId
|
||||
* @returns
|
||||
*/
|
||||
export async function getImageHistory(
|
||||
environmentId: EnvironmentId,
|
||||
id: ImageLayer['Id']
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<ImageLayer[]>(
|
||||
buildDockerProxyUrl(environmentId, 'images', id, 'history')
|
||||
);
|
||||
return data;
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err as Error, 'Unable to retrieve image layers');
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ import { ImageSummary } from 'docker-types/generated/1.41';
|
|||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildUrl } from '../build-url';
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
|
||||
import { queryKeys } from './queryKeys';
|
||||
|
||||
|
@ -24,10 +24,15 @@ export function useImages<T = ImagesListResponse>(
|
|||
);
|
||||
}
|
||||
|
||||
async function getImages(environmentId: EnvironmentId) {
|
||||
/**
|
||||
* Raw docker API proxy
|
||||
* @param environmentId
|
||||
* @returns
|
||||
*/
|
||||
export async function getImages(environmentId: EnvironmentId) {
|
||||
try {
|
||||
const { data } = await axios.get<ImagesListResponse>(
|
||||
buildUrl(environmentId, 'images', 'json')
|
||||
buildDockerProxyUrl(environmentId, 'images', 'json')
|
||||
);
|
||||
return data;
|
||||
} catch (err) {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import { ImageId, ImageName } from '@/docker/models/image';
|
||||
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
|
||||
/**
|
||||
* Raw docker API proxy
|
||||
* @param environmentId
|
||||
* @param id
|
||||
* @param force
|
||||
* @returns
|
||||
*/
|
||||
export async function removeImage(
|
||||
environmentId: EnvironmentId,
|
||||
id: ImageId | ImageName,
|
||||
force?: boolean
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.delete(
|
||||
buildDockerProxyUrl(environmentId, 'images', id),
|
||||
{ params: { force } }
|
||||
);
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e, 'Unable to remove image');
|
||||
}
|
||||
}
|
29
app/react/docker/proxy/queries/images/useTagImageMutation.ts
Normal file
29
app/react/docker/proxy/queries/images/useTagImageMutation.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { ImageId, ImageName } from '@/docker/models/image';
|
||||
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
|
||||
/**
|
||||
* Raw docker API proxy
|
||||
* @param environmentId
|
||||
* @param id
|
||||
* @param repo string generated by `buildImageFullURIFromModel` or `buildImageFullURI`
|
||||
* @returns
|
||||
*/
|
||||
export async function tagImage(
|
||||
environmentId: EnvironmentId,
|
||||
id: ImageId | ImageName,
|
||||
repo: string
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.post(
|
||||
buildDockerProxyUrl(environmentId, 'images', id, 'tag'),
|
||||
{},
|
||||
{ params: { repo } }
|
||||
);
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e, 'Unable to tag image');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
|
||||
/**
|
||||
* Raw docker API proxy
|
||||
* @param environmentId
|
||||
* @param file
|
||||
* @returns
|
||||
*/
|
||||
export async function uploadImages(environmentId: EnvironmentId, file: File) {
|
||||
try {
|
||||
return await axios.post(
|
||||
buildDockerProxyUrl(environmentId, 'images', 'load'),
|
||||
file,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': file.type, // 'application/x-tar',
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e, 'Unable to upload image');
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildUrl as buildProxyUrl } from '../build-url';
|
||||
|
||||
export function buildUrl(
|
||||
environmentId: EnvironmentId,
|
||||
action?: string,
|
||||
subAction = ''
|
||||
) {
|
||||
return buildProxyUrl(
|
||||
environmentId,
|
||||
'nodes',
|
||||
subAction ? `${action}/${subAction}` : action
|
||||
);
|
||||
}
|
26
app/react/docker/proxy/queries/nodes/useNode.ts
Normal file
26
app/react/docker/proxy/queries/nodes/useNode.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { Node } from 'docker-types/generated/1.41';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
|
||||
/**
|
||||
* Raw docker API proxy
|
||||
* @param environmentId
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
export async function getNode(
|
||||
environmentId: EnvironmentId,
|
||||
id: NonNullable<Node['ID']>
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<Node>(
|
||||
buildDockerProxyUrl(environmentId, 'nodes', id)
|
||||
);
|
||||
return data;
|
||||
} catch (error) {
|
||||
throw parseAxiosError(error, 'Unable to retrieve node');
|
||||
}
|
||||
}
|
|
@ -4,16 +4,24 @@ import { useQuery } from 'react-query';
|
|||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildUrl } from './build-url';
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
|
||||
import { queryKeys } from './query-keys';
|
||||
|
||||
export function useNodes(environmentId: EnvironmentId) {
|
||||
return useQuery(queryKeys.base(environmentId), () => getNodes(environmentId));
|
||||
}
|
||||
|
||||
async function getNodes(environmentId: EnvironmentId) {
|
||||
/**
|
||||
* Raw docker API proxy
|
||||
* @param environmentId
|
||||
* @returns
|
||||
*/
|
||||
export async function getNodes(environmentId: EnvironmentId) {
|
||||
try {
|
||||
const { data } = await axios.get<Array<Node>>(buildUrl(environmentId));
|
||||
const { data } = await axios.get<Array<Node>>(
|
||||
buildDockerProxyUrl(environmentId, 'nodes')
|
||||
);
|
||||
return data;
|
||||
} catch (error) {
|
||||
throw parseAxiosError(error, 'Unable to retrieve nodes');
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import { Node, NodeSpec } from 'docker-types/generated/1.41';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
|
||||
/**
|
||||
* Raw docker API proxy
|
||||
* @param environmentId
|
||||
* @param id
|
||||
* @param node
|
||||
* @param version
|
||||
*/
|
||||
export async function updateNode(
|
||||
environmentId: EnvironmentId,
|
||||
id: NonNullable<Node['ID']>,
|
||||
node: NodeSpec,
|
||||
version: number
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.post(
|
||||
buildDockerProxyUrl(environmentId, 'nodes', id, 'update'),
|
||||
node,
|
||||
{ params: { version } }
|
||||
);
|
||||
return data;
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err, 'Unable to update node');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import { SecretSpec } from 'docker-types/generated/1.41';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
|
||||
export async function createSecret(
|
||||
environmentId: EnvironmentId,
|
||||
secret: SecretSpec
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.post(
|
||||
buildDockerProxyUrl(environmentId, 'secrets', 'create'),
|
||||
secret
|
||||
);
|
||||
return data;
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err, 'Unable to create secret');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import { Secret } from 'docker-types/generated/1.41';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
|
||||
export async function removeSecret(
|
||||
environmentId: EnvironmentId,
|
||||
id: NonNullable<Secret['ID']>
|
||||
) {
|
||||
try {
|
||||
await axios.delete(buildDockerProxyUrl(environmentId, 'secrets', id));
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err, 'Unable to remove secret');
|
||||
}
|
||||
}
|
21
app/react/docker/proxy/queries/secrets/useSecret.ts
Normal file
21
app/react/docker/proxy/queries/secrets/useSecret.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { Secret } from 'docker-types/generated/1.41';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import { PortainerResponse } from '@/react/docker/types';
|
||||
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
|
||||
export async function getSecret(
|
||||
environmentId: EnvironmentId,
|
||||
id: NonNullable<Secret['ID']>
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<PortainerResponse<Secret>>(
|
||||
buildDockerProxyUrl(environmentId, 'secrets', id)
|
||||
);
|
||||
return data;
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err, 'Unable to retrieve secret');
|
||||
}
|
||||
}
|
15
app/react/docker/proxy/queries/secrets/useSecrets.ts
Normal file
15
app/react/docker/proxy/queries/secrets/useSecrets.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
|
||||
export async function getSecrets(environmentId: EnvironmentId) {
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
buildDockerProxyUrl(environmentId, 'secrets')
|
||||
);
|
||||
return data;
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err, 'Unable to retrieve secrets');
|
||||
}
|
||||
}
|
35
app/react/docker/proxy/queries/tasks/useTasks.ts
Normal file
35
app/react/docker/proxy/queries/tasks/useTasks.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { Task } from 'docker-types/generated/1.41';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildDockerProxyUrl } from '../buildDockerProxyUrl';
|
||||
import { withFiltersQueryParam } from '../utils';
|
||||
|
||||
type Filters = {
|
||||
'desired-state'?: 'running' | 'shutdown' | 'accepted';
|
||||
id?: Task['ID'];
|
||||
label?: Task['Labels'];
|
||||
name?: Task['Name'];
|
||||
node?: Task['NodeID'];
|
||||
service?: Task['ServiceID'];
|
||||
};
|
||||
|
||||
export async function getTasks(
|
||||
environmentId: EnvironmentId,
|
||||
filters?: Filters
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<Task[]>(
|
||||
buildDockerProxyUrl(environmentId, 'tasks'),
|
||||
{
|
||||
params: {
|
||||
...withFiltersQueryParam(filters),
|
||||
},
|
||||
}
|
||||
);
|
||||
return data;
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err, 'Unable to retrieve tasks');
|
||||
}
|
||||
}
|
32
app/react/docker/proxy/queries/useCommitContainerMutation.ts
Normal file
32
app/react/docker/proxy/queries/useCommitContainerMutation.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
|
||||
|
||||
type CommitParams = {
|
||||
container?: string; // The ID or name of the container to commit
|
||||
repo?: string; // Repository name for the created image
|
||||
tag?: string; // Tag name for the create image
|
||||
comment?: string; // Commit message
|
||||
author?: string; // Author of the image (e.g., John Hannibal Smith <hannibal@a-team.com>)
|
||||
pause?: boolean; // Default: true Whether to pause the container before committing
|
||||
changes?: string; // Dockerfile instructions to apply while committing
|
||||
};
|
||||
|
||||
export async function commitContainer(
|
||||
environmentId: EnvironmentId,
|
||||
params: CommitParams
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.post<{ Id: string }>(
|
||||
buildDockerProxyUrl(environmentId, 'commit'),
|
||||
{},
|
||||
{
|
||||
params,
|
||||
}
|
||||
);
|
||||
return data;
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err, 'Unable to commit container');
|
||||
}
|
||||
}
|
30
app/react/docker/proxy/queries/useEvents.ts
Normal file
30
app/react/docker/proxy/queries/useEvents.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { EventMessage } from 'docker-types/generated/1.41';
|
||||
|
||||
import axios, {
|
||||
jsonObjectsToArrayHandler,
|
||||
parseAxiosError,
|
||||
} from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
|
||||
|
||||
/**
|
||||
* Raw docker API proxy
|
||||
* @param environmentId
|
||||
* @param param1
|
||||
* @returns
|
||||
*/
|
||||
export async function getEvents(
|
||||
environmentId: EnvironmentId,
|
||||
{ since, until }: { since: string; until: string }
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<EventMessage[]>(
|
||||
buildDockerProxyUrl(environmentId, 'events'),
|
||||
{ params: { since, until }, transformResponse: jsonObjectsToArrayHandler }
|
||||
);
|
||||
return data;
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err, 'Unable to retrieve engine events');
|
||||
}
|
||||
}
|
30
app/react/docker/proxy/queries/useExecResizeTTYMutation.ts
Normal file
30
app/react/docker/proxy/queries/useExecResizeTTYMutation.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
|
||||
|
||||
/**
|
||||
* Raw docker API proxy
|
||||
* @param environmentId
|
||||
* @param id exec instance id
|
||||
*/
|
||||
export async function resizeTTY(
|
||||
environmentId: EnvironmentId,
|
||||
id: string,
|
||||
{ width, height }: { width: number; height: number }
|
||||
) {
|
||||
try {
|
||||
await axios.post(
|
||||
buildDockerProxyUrl(environmentId, 'exec', id, 'resize'),
|
||||
{},
|
||||
{
|
||||
params: {
|
||||
h: height,
|
||||
w: width,
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err, 'Unable to resize tty of exec');
|
||||
}
|
||||
}
|
|
@ -4,16 +4,16 @@ import { SystemInfo } from 'docker-types/generated/1.41';
|
|||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildUrl } from './build-url';
|
||||
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
|
||||
|
||||
export async function getInfo(environmentId: EnvironmentId) {
|
||||
try {
|
||||
const { data } = await axios.get<SystemInfo>(
|
||||
buildUrl(environmentId, 'info')
|
||||
buildDockerProxyUrl(environmentId, 'info')
|
||||
);
|
||||
return data;
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err as Error, 'Unable to retrieve version');
|
||||
throw parseAxiosError(err, 'Unable to retrieve system info');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
12
app/react/docker/proxy/queries/usePing.ts
Normal file
12
app/react/docker/proxy/queries/usePing.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
|
||||
|
||||
export async function ping(environmentId: EnvironmentId) {
|
||||
try {
|
||||
await axios.get(buildDockerProxyUrl(environmentId, '_ping'));
|
||||
} catch (error) {
|
||||
throw parseAxiosError(error);
|
||||
}
|
||||
}
|
|
@ -10,13 +10,19 @@ import { EnvironmentId } from '@/react/portainer/environments/types';
|
|||
|
||||
import { queryKeys } from '../../queries/utils/root';
|
||||
|
||||
import { buildUrl } from './build-url';
|
||||
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
|
||||
import { useInfo } from './useInfo';
|
||||
|
||||
const pluginTypeToVersionMap: { [k in keyof PluginsInfo]: string } = {
|
||||
Volume: 'docker.volumedriver/1.0',
|
||||
Network: 'docker.networkdriver/1.0',
|
||||
Log: 'docker.logdriver/1.0',
|
||||
};
|
||||
|
||||
export async function getPlugins(environmentId: EnvironmentId) {
|
||||
try {
|
||||
const { data } = await axios.get<Array<Plugin>>(
|
||||
buildUrl(environmentId, 'plugins')
|
||||
buildDockerProxyUrl(environmentId, 'plugins')
|
||||
);
|
||||
return data;
|
||||
} catch (e) {
|
||||
|
@ -38,77 +44,74 @@ function usePlugins(
|
|||
export function useServicePlugins(
|
||||
environmentId: EnvironmentId,
|
||||
systemOnly: boolean,
|
||||
pluginType: keyof PluginsInfo,
|
||||
pluginVersion: string
|
||||
pluginType: keyof PluginsInfo
|
||||
) {
|
||||
const systemPluginsQuery = useInfo(environmentId, (info) => info.Plugins);
|
||||
const pluginsQuery = usePlugins(environmentId, { enabled: !systemOnly });
|
||||
|
||||
return {
|
||||
data: aggregateData(),
|
||||
data: aggregateData(
|
||||
systemPluginsQuery.data,
|
||||
pluginsQuery.data,
|
||||
systemOnly,
|
||||
pluginType
|
||||
),
|
||||
isLoading: systemPluginsQuery.isLoading || pluginsQuery.isLoading,
|
||||
};
|
||||
}
|
||||
|
||||
function aggregateData() {
|
||||
if (!systemPluginsQuery.data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const systemPlugins = systemPluginsQuery.data[pluginType] || [];
|
||||
|
||||
if (systemOnly) {
|
||||
return systemPlugins;
|
||||
}
|
||||
|
||||
const plugins =
|
||||
pluginsQuery.data
|
||||
?.filter(
|
||||
(plugin) =>
|
||||
plugin.Enabled &&
|
||||
// docker has an error in their types, so we need to cast to unknown first
|
||||
// see https://docs.docker.com/engine/api/v1.41/#tag/Plugin/operation/PluginList
|
||||
plugin.Config.Interface.Types.includes(
|
||||
pluginVersion as unknown as PluginInterfaceType
|
||||
)
|
||||
)
|
||||
.map((plugin) => plugin.Name) || [];
|
||||
|
||||
return [...systemPlugins, ...plugins];
|
||||
/**
|
||||
* @private Exported only for AngularJS `PluginService` factory `app/docker/services/pluginService.js`
|
||||
*/
|
||||
export function aggregateData(
|
||||
systemPluginsData: PluginsInfo | undefined,
|
||||
pluginsData: Plugin[] | undefined,
|
||||
systemOnly: boolean,
|
||||
pluginType: keyof PluginsInfo
|
||||
) {
|
||||
if (!systemPluginsData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const systemPlugins = systemPluginsData[pluginType] || [];
|
||||
|
||||
if (systemOnly) {
|
||||
return systemPlugins;
|
||||
}
|
||||
|
||||
const plugins =
|
||||
pluginsData
|
||||
?.filter(
|
||||
(plugin) =>
|
||||
plugin.Enabled &&
|
||||
// docker has an error in their types, so we need to cast to unknown first
|
||||
// see https://docs.docker.com/engine/api/v1.41/#tag/Plugin/operation/PluginList
|
||||
plugin.Config.Interface.Types.includes(
|
||||
pluginTypeToVersionMap[pluginType] as unknown as PluginInterfaceType
|
||||
)
|
||||
)
|
||||
.map((plugin) => plugin.Name) || [];
|
||||
|
||||
return [...systemPlugins, ...plugins];
|
||||
}
|
||||
|
||||
export function useLoggingPlugins(
|
||||
environmentId: EnvironmentId,
|
||||
systemOnly: boolean
|
||||
) {
|
||||
return useServicePlugins(
|
||||
environmentId,
|
||||
systemOnly,
|
||||
'Log',
|
||||
'docker.logdriver/1.0'
|
||||
);
|
||||
return useServicePlugins(environmentId, systemOnly, 'Log');
|
||||
}
|
||||
|
||||
export function useVolumePlugins(
|
||||
environmentId: EnvironmentId,
|
||||
systemOnly: boolean
|
||||
) {
|
||||
return useServicePlugins(
|
||||
environmentId,
|
||||
systemOnly,
|
||||
'Volume',
|
||||
'docker.volumedriver/1.0'
|
||||
);
|
||||
return useServicePlugins(environmentId, systemOnly, 'Volume');
|
||||
}
|
||||
|
||||
export function useNetworkPlugins(
|
||||
environmentId: EnvironmentId,
|
||||
systemOnly: boolean
|
||||
) {
|
||||
return useServicePlugins(
|
||||
environmentId,
|
||||
systemOnly,
|
||||
'Network',
|
||||
'docker.networkdriver/1.0'
|
||||
);
|
||||
return useServicePlugins(environmentId, systemOnly, 'Network');
|
||||
}
|
||||
|
|
40
app/react/docker/proxy/queries/useSwarm.ts
Normal file
40
app/react/docker/proxy/queries/useSwarm.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { useQuery } from 'react-query';
|
||||
import { Swarm } from 'docker-types/generated/1.41';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { queryKeys } from './query-keys';
|
||||
import { useIsSwarm } from './useInfo';
|
||||
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
|
||||
|
||||
export function useSwarm<T = Swarm>(
|
||||
environmentId: EnvironmentId,
|
||||
{ select }: { select?(value: Swarm): T } = {}
|
||||
) {
|
||||
const isSwarm = useIsSwarm(environmentId);
|
||||
|
||||
return useQuery({
|
||||
queryKey: [...queryKeys.base(environmentId), 'swarm'] as const,
|
||||
queryFn: () => getSwarm(environmentId),
|
||||
select,
|
||||
enabled: isSwarm,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getSwarm(environmentId: EnvironmentId) {
|
||||
try {
|
||||
const { data } = await axios.get<Swarm>(
|
||||
buildDockerProxyUrl(environmentId, 'swarm')
|
||||
);
|
||||
return data;
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err, 'Unable to retrieve swarm information');
|
||||
}
|
||||
}
|
||||
|
||||
export function useSwarmId(environmentId: EnvironmentId) {
|
||||
return useSwarm(environmentId, {
|
||||
select: (swarm) => swarm.ID,
|
||||
});
|
||||
}
|
|
@ -4,12 +4,12 @@ import { SystemVersion } from 'docker-types/generated/1.41';
|
|||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildUrl } from './build-url';
|
||||
import { buildDockerProxyUrl } from './buildDockerProxyUrl';
|
||||
|
||||
export async function getVersion(environmentId: EnvironmentId) {
|
||||
try {
|
||||
const { data } = await axios.get<SystemVersion>(
|
||||
buildUrl(environmentId, 'version')
|
||||
buildDockerProxyUrl(environmentId, 'version')
|
||||
);
|
||||
return data;
|
||||
} catch (err) {
|
||||
|
|
67
app/react/docker/proxy/queries/utils.ts
Normal file
67
app/react/docker/proxy/queries/utils.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
import { agentTargetHeader } from '@/portainer/services/axios';
|
||||
import { RegistryId } from '@/react/portainer/registries/types/registry';
|
||||
|
||||
/**
|
||||
* Generates the `filters` query param entry for docker API list actions
|
||||
*
|
||||
* @param filters map[string][]string
|
||||
* @returns `{ filters: filters && JSON.stringify(filters) }`
|
||||
*/
|
||||
export function withFiltersQueryParam<T = unknown>(filters?: T) {
|
||||
return { filters: filters && JSON.stringify(filters) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the registry credentials in base64
|
||||
* @param registryId
|
||||
* @returns
|
||||
*/
|
||||
function encodeRegistryCredentials(registryId: RegistryId) {
|
||||
return window.btoa(JSON.stringify({ registryId }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the `X-Registry-Auth` header for docker API
|
||||
* @param registryId The registry Id to use
|
||||
* @returns
|
||||
*/
|
||||
export function withRegistryAuthHeader(registryId?: RegistryId) {
|
||||
return registryId !== undefined
|
||||
? { 'X-Registry-Auth': encodeRegistryCredentials(registryId) }
|
||||
: {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the `X-PortainerAgent-Target` header
|
||||
* @param nodeName The node name to target
|
||||
* @returns
|
||||
*/
|
||||
export function withAgentTargetHeader(nodeName?: string) {
|
||||
return nodeName ? { [agentTargetHeader]: nodeName } : {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the `'X-PortainerAgent-ManagerOperation'` header
|
||||
* @param managerOperation
|
||||
* @returns
|
||||
*/
|
||||
export function withAgentManagerOperationHeader(managerOperation?: boolean) {
|
||||
return managerOperation ? { 'X-PortainerAgent-ManagerOperation': '1' } : {};
|
||||
}
|
||||
|
||||
/**
|
||||
* The Docker API expects array query params as `param = value_1 & param = value_2`
|
||||
* axios default serializer generates `names[] = value_1 & names[] = value_2`
|
||||
* which are ignored by the Docker API
|
||||
* @param params
|
||||
* @returns the concatenated string of query params with Docker expected format
|
||||
*/
|
||||
export function formatArrayQueryParamsForDockerAPI(
|
||||
params: Record<string, unknown>
|
||||
) {
|
||||
return Object.entries(params)
|
||||
.flatMap(([param, value]) =>
|
||||
(Array.isArray(value) ? value : [value]).map((v) => `${param}=${v}`)
|
||||
)
|
||||
.join('&');
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue