mirror of
https://github.com/portainer/portainer.git
synced 2025-08-05 05:45:22 +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,23 +0,0 @@
|
|||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import { buildUrl as buildDockerUrl } from '@/react/docker/queries/utils/build-url';
|
||||
import { buildUrl as buildDockerProxyUrl } from '@/react/docker/proxy/queries/build-url';
|
||||
|
||||
export function buildUrl(environmentId: EnvironmentId) {
|
||||
return buildDockerUrl(environmentId, 'images');
|
||||
}
|
||||
|
||||
export function buildProxyUrl(
|
||||
environmentId: EnvironmentId,
|
||||
{ id, action }: { id?: string; action?: string } = {}
|
||||
) {
|
||||
let dockerAction = '';
|
||||
if (id) {
|
||||
dockerAction += `${id}`;
|
||||
}
|
||||
|
||||
if (action) {
|
||||
dockerAction = dockerAction ? `${dockerAction}/${action}` : action;
|
||||
}
|
||||
|
||||
return buildDockerProxyUrl(environmentId, 'images', dockerAction);
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import { Registry } from '@/react/portainer/registries/types/registry';
|
||||
|
||||
/**
|
||||
* Encodes the registry credentials in base64
|
||||
* @param registryId
|
||||
* @returns
|
||||
*/
|
||||
export function encodeRegistryCredentials(registryId: Registry['Id']) {
|
||||
const credentials = {
|
||||
registryId,
|
||||
};
|
||||
|
||||
return window.btoa(JSON.stringify(credentials));
|
||||
}
|
285
app/react/docker/images/queries/useBuildImageMutation.ts
Normal file
285
app/react/docker/images/queries/useBuildImageMutation.ts
Normal file
|
@ -0,0 +1,285 @@
|
|||
import axios, {
|
||||
jsonObjectsToArrayHandler,
|
||||
parseAxiosError,
|
||||
} from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { buildDockerProxyUrl } from '../../proxy/queries/buildDockerProxyUrl';
|
||||
import { formatArrayQueryParamsForDockerAPI } from '../../proxy/queries/utils';
|
||||
|
||||
export async function buildImageFromUpload(
|
||||
environmentId: EnvironmentId,
|
||||
names: string[],
|
||||
file: File,
|
||||
path: string
|
||||
) {
|
||||
return buildImage(
|
||||
environmentId,
|
||||
{ t: names, dockerfile: path },
|
||||
file,
|
||||
file.type
|
||||
);
|
||||
}
|
||||
|
||||
export async function buildImageFromURL(
|
||||
environmentId: EnvironmentId,
|
||||
names: string[],
|
||||
url: string,
|
||||
path: string
|
||||
) {
|
||||
return buildImage(
|
||||
environmentId,
|
||||
{ t: names, remote: url, dockerfile: path },
|
||||
{},
|
||||
'application/x-tar'
|
||||
);
|
||||
}
|
||||
|
||||
export async function buildImageFromDockerfileContent(
|
||||
environmentId: EnvironmentId,
|
||||
names: string[],
|
||||
content: string
|
||||
) {
|
||||
return buildImage(
|
||||
environmentId,
|
||||
{ t: names },
|
||||
{ content },
|
||||
'application/json'
|
||||
);
|
||||
}
|
||||
|
||||
export async function buildImageFromDockerfileContentAndFiles(
|
||||
environmentId: EnvironmentId,
|
||||
names: string[],
|
||||
content: string,
|
||||
files: File[]
|
||||
) {
|
||||
const dockerfile = new Blob([content], { type: 'text/plain' });
|
||||
const uploadFiles = [dockerfile, ...files];
|
||||
|
||||
return buildImage(
|
||||
environmentId,
|
||||
{ t: names },
|
||||
{ file: uploadFiles },
|
||||
'multipart/form-data'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw docker API proxy
|
||||
*
|
||||
* -----
|
||||
*
|
||||
* See api/http/proxy/factory/docker/build.go for the rules (copied below)
|
||||
*
|
||||
* buildOperation inspects the "Content-Type" header to determine if it needs to alter the request.
|
||||
*
|
||||
* -- buildImageFromUpload()
|
||||
* If the value of the header is empty, it means that a Dockerfile is posted via upload, the function
|
||||
* will extract the file content from the request body, tar it, and rewrite the body.
|
||||
* !! THIS IS ONLY TRUE WHEN THE UPLOADED DOCKERFILE FILE HAS NO EXTENSION (the generated file.type in the frontend will be empty)
|
||||
* If the Dockerfile is named like Dockerfile.yaml or has an internal type, a non-empty Content-Type header will be generated
|
||||
*
|
||||
* -- buildImageFromDockerfileContent()
|
||||
* If the value of the header contains "application/json", it means that the content of a Dockerfile is posted
|
||||
* in the request payload as JSON, the function will create a new file called Dockerfile inside a tar archive and
|
||||
* rewrite the body of the request.
|
||||
*
|
||||
* -- buildImageFromUpload()
|
||||
* -- buildImageFromURL()
|
||||
* -- buildImageFromDockerfileContentAndFiles()
|
||||
* In any other case, it will leave the request unaltered.
|
||||
*
|
||||
* -----
|
||||
*
|
||||
* @param environmentId
|
||||
* @param params
|
||||
* @param payload
|
||||
* @param contentType
|
||||
*/
|
||||
async function buildImage(
|
||||
environmentId: EnvironmentId,
|
||||
params: BuildImageQueryParams,
|
||||
payload: unknown,
|
||||
contentType: string
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.post(
|
||||
buildDockerProxyUrl(environmentId, 'build'),
|
||||
payload,
|
||||
{
|
||||
headers: { 'Content-Type': contentType },
|
||||
params,
|
||||
transformResponse: jsonObjectsToArrayHandler,
|
||||
paramsSerializer: formatArrayQueryParamsForDockerAPI,
|
||||
}
|
||||
);
|
||||
return data;
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err, 'Unable to build image');
|
||||
}
|
||||
}
|
||||
|
||||
type BuildImageQueryParams = {
|
||||
/**
|
||||
* Path within the build context to the Dockerfile.
|
||||
* This is ignored if remote is specified and points to an external Dockerfile.
|
||||
*
|
||||
* @default "Dockerfile"
|
||||
*/
|
||||
dockerfile?: string;
|
||||
|
||||
/**
|
||||
* A name and optional tag to apply to the image in the name:tag format.
|
||||
* If you omit the tag the default latest value is assumed.
|
||||
* You can provide several t parameters.
|
||||
*/
|
||||
t?: string[];
|
||||
|
||||
/**
|
||||
* Extra hosts to add to /etc/hosts
|
||||
*/
|
||||
extrahost?: string;
|
||||
|
||||
/**
|
||||
* A Git repository URI or HTTP/HTTPS context URI.
|
||||
* If the URI points to a single text file, the file’s contents are placed into a file called Dockerfile and the image is built from that file.
|
||||
* If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build.
|
||||
* If the URI points to a tarball and the dockerfile parameter is also specified, there must be a file with the corresponding path inside the tarball.
|
||||
*/
|
||||
remote?: string;
|
||||
|
||||
/**
|
||||
* Suppress verbose build output.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
q?: boolean;
|
||||
|
||||
/**
|
||||
* Do not use the cache when building the image.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
nocache?: boolean;
|
||||
|
||||
/**
|
||||
* JSON array of images used for build cache resolution.
|
||||
*/
|
||||
cachefrom?: string[];
|
||||
|
||||
/**
|
||||
* Attempt to pull the image even if an older image exists locally.
|
||||
*/
|
||||
pull?: string;
|
||||
|
||||
/**
|
||||
* Remove intermediate containers after a successful build.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
rm?: boolean;
|
||||
|
||||
/**
|
||||
* Always remove intermediate containers, even upon failure.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
forcerm?: boolean;
|
||||
|
||||
/**
|
||||
* Set memory limit for build.
|
||||
*/
|
||||
memory?: number;
|
||||
|
||||
/**
|
||||
* Total memory (memory + swap).
|
||||
*
|
||||
* Set as -1 to disable swap.
|
||||
*/
|
||||
memswap?: number;
|
||||
|
||||
/**
|
||||
* CPU shares (relative weight).
|
||||
*/
|
||||
cpushares?: number;
|
||||
|
||||
/**
|
||||
* CPUs in which to allow execution (e.g., 0-3, 0,1).
|
||||
*/
|
||||
cpusetcpus?: string;
|
||||
|
||||
/**
|
||||
* The length of a CPU period in microseconds.
|
||||
*/
|
||||
cpuperiod?: number;
|
||||
|
||||
/**
|
||||
* Microseconds of CPU time that the container can get in a CPU period.
|
||||
*/
|
||||
cpuquota?: number;
|
||||
|
||||
/**
|
||||
* JSON map of string pairs for build-time variables. Users pass these values at build-time.
|
||||
* Docker uses the buildargs as the environment context for commands run via the Dockerfile RUN instruction, or for variable expansion in other Dockerfile instructions.
|
||||
* This is not meant for passing secret values.
|
||||
* For example, the build arg FOO=bar would become {"FOO":"bar"} in JSON. This would result in the query parameter buildargs={"FOO":"bar"}.
|
||||
* Note that {"FOO":"bar"} should be URI component encoded.
|
||||
* Read more about the buildargs instruction.
|
||||
*/
|
||||
buildargs?: string;
|
||||
|
||||
/**
|
||||
* Size of /dev/shm in bytes. The size must be greater than 0. If omitted the system uses 64MB.
|
||||
*/
|
||||
shmsize?: number;
|
||||
|
||||
/**
|
||||
* Squash the resulting images layers into a single layer. (Experimental release only.)
|
||||
*/
|
||||
squash?: boolean;
|
||||
|
||||
/**
|
||||
* Arbitrary key/value labels to set on the image, as a JSON map of string pairs.
|
||||
*/
|
||||
labels?: Record<string, string>;
|
||||
|
||||
/**
|
||||
* Sets the networking mode for the run commands during build. Supported standard values are: bridge, host, none, and container:<name|id>.
|
||||
* Any other value is taken as a custom network's name or ID to which this container should connect to.
|
||||
*/
|
||||
networkmode?: string;
|
||||
|
||||
/**
|
||||
* Platform in the format os[/arch[/variant]]
|
||||
*
|
||||
* @default ""
|
||||
*/
|
||||
platform?: string;
|
||||
|
||||
/**
|
||||
* Target build stage
|
||||
*
|
||||
* @default ""
|
||||
*/
|
||||
target?: string;
|
||||
|
||||
/**
|
||||
* BuildKit output configuration
|
||||
*
|
||||
* @default ""
|
||||
*/
|
||||
outputs?: string;
|
||||
|
||||
/**
|
||||
* Version of the builder backend to use.
|
||||
*
|
||||
* @enum {('1' | '2')}
|
||||
*
|
||||
* @default '1'
|
||||
*
|
||||
* - 1 is the first generation classic (deprecated) builder in the Docker daemon (default)
|
||||
* - 2 is BuildKit
|
||||
*/
|
||||
version?: string;
|
||||
};
|
|
@ -3,7 +3,8 @@ 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 { buildDockerUrl } from '../../queries/utils/buildDockerUrl';
|
||||
|
||||
import { queryKeys } from './queryKeys';
|
||||
|
||||
export interface ImagesListResponse {
|
||||
|
@ -20,6 +21,11 @@ export interface ImagesListResponse {
|
|||
used: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in ImagesDatatable
|
||||
*
|
||||
* Query /api/docker/{envId}/images
|
||||
*/
|
||||
export function useImages<T = Array<ImagesListResponse>>(
|
||||
environmentId: EnvironmentId,
|
||||
withUsage = false,
|
||||
|
@ -46,7 +52,7 @@ async function getImages(
|
|||
) {
|
||||
try {
|
||||
const { data } = await axios.get<Array<ImagesListResponse>>(
|
||||
buildUrl(environmentId),
|
||||
buildDockerUrl(environmentId, 'images'),
|
||||
{ params: { withUsage } }
|
||||
);
|
||||
return data;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { RawAxiosRequestHeaders } from 'axios';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import { Registry } from '@/react/portainer/registries/types/registry';
|
||||
|
||||
import { buildImageFullURI } from '../utils';
|
||||
|
||||
import { encodeRegistryCredentials } from './encodeRegistryCredentials';
|
||||
import { buildProxyUrl } from './build-url';
|
||||
import {
|
||||
withRegistryAuthHeader,
|
||||
withAgentTargetHeader,
|
||||
} from '../../proxy/queries/utils';
|
||||
import { buildDockerProxyUrl } from '../../proxy/queries/buildDockerProxyUrl';
|
||||
|
||||
interface PullImageOptions {
|
||||
environmentId: EnvironmentId;
|
||||
|
@ -24,33 +24,27 @@ export async function pullImage({
|
|||
nodeName,
|
||||
registry,
|
||||
}: PullImageOptions) {
|
||||
const authenticationDetails =
|
||||
registry && registry.Authentication
|
||||
? encodeRegistryCredentials(registry.Id)
|
||||
: '';
|
||||
|
||||
const imageURI = buildImageFullURI(image, registry);
|
||||
|
||||
const headers: RawAxiosRequestHeaders = {
|
||||
'X-Registry-Auth': authenticationDetails,
|
||||
};
|
||||
|
||||
if (nodeName) {
|
||||
headers['X-PortainerAgent-Target'] = nodeName;
|
||||
}
|
||||
|
||||
try {
|
||||
await axios.post(buildProxyUrl(environmentId, { action: 'create' }), null, {
|
||||
params: {
|
||||
fromImage: imageURI,
|
||||
},
|
||||
headers,
|
||||
});
|
||||
await axios.post(
|
||||
buildDockerProxyUrl(environmentId, 'images', 'create'),
|
||||
null,
|
||||
{
|
||||
params: {
|
||||
fromImage: imageURI,
|
||||
},
|
||||
headers: {
|
||||
...withRegistryAuthHeader(registry?.Id),
|
||||
...withAgentTargetHeader(nodeName),
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
if (ignoreErrors) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw parseAxiosError(err as Error, 'Unable to pull image');
|
||||
throw parseAxiosError(err, 'Unable to pull image');
|
||||
}
|
||||
}
|
||||
|
|
42
app/react/docker/images/queries/usePushImageMutation.ts
Normal file
42
app/react/docker/images/queries/usePushImageMutation.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import axios, {
|
||||
jsonObjectsToArrayHandler,
|
||||
parseAxiosError,
|
||||
} from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import { Registry } from '@/react/portainer/registries/types/registry';
|
||||
|
||||
import { buildImageFullURI } from '../utils';
|
||||
import { withRegistryAuthHeader } from '../../proxy/queries/utils';
|
||||
import { buildDockerProxyUrl } from '../../proxy/queries/buildDockerProxyUrl';
|
||||
|
||||
interface PushImageOptions {
|
||||
environmentId: EnvironmentId;
|
||||
image: string;
|
||||
registry?: Registry;
|
||||
}
|
||||
|
||||
export async function pushImage({
|
||||
environmentId,
|
||||
image,
|
||||
registry,
|
||||
}: PushImageOptions) {
|
||||
const imageURI = buildImageFullURI(image, registry);
|
||||
|
||||
try {
|
||||
const { data } = await axios.post(
|
||||
buildDockerProxyUrl(environmentId, 'images', imageURI, 'push'),
|
||||
null,
|
||||
{
|
||||
headers: {
|
||||
...withRegistryAuthHeader(registry?.Id),
|
||||
},
|
||||
transformResponse: jsonObjectsToArrayHandler,
|
||||
}
|
||||
);
|
||||
if (data[data.length - 1].error) {
|
||||
throw new Error(data[data.length - 1].error);
|
||||
}
|
||||
} catch (err) {
|
||||
throw parseAxiosError(err, 'Unable to push image');
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue