mirror of https://github.com/portainer/portainer
refactor(environments): remove angular dep from service [EE-2346] (#6360)
refactor(environments): parse axios errorpull/6361/head
parent
ecd0eb6170
commit
07c6ce84c2
|
@ -0,0 +1,174 @@
|
|||
import PortainerError from '@/portainer/error';
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
|
||||
import {
|
||||
Environment,
|
||||
EnvironmentGroupId,
|
||||
EnvironmentCreationTypes,
|
||||
TagId,
|
||||
} from '../types';
|
||||
|
||||
import { arrayToJson, buildUrl, json2formData } from './utils';
|
||||
|
||||
export async function createLocalEndpoint(
|
||||
name = 'local',
|
||||
url = '',
|
||||
publicUrl = '',
|
||||
groupId: EnvironmentGroupId = 1,
|
||||
tagIds: TagId[] = []
|
||||
) {
|
||||
let endpointUrl = url;
|
||||
if (endpointUrl !== '') {
|
||||
if (endpointUrl.includes('//./pipe/')) {
|
||||
endpointUrl = `unix://${url}`;
|
||||
} else {
|
||||
// Windows named pipe
|
||||
endpointUrl = `npipe://${url}`;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return await createEndpoint(
|
||||
name,
|
||||
EnvironmentCreationTypes.LocalDockerEnvironment,
|
||||
{ url: endpointUrl, publicUrl, groupId, tagIds }
|
||||
);
|
||||
} catch (err) {
|
||||
throw new PortainerError('Unable to create environment', err as Error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function createLocalKubernetesEndpoint(
|
||||
name = 'local',
|
||||
tagIds: TagId[] = []
|
||||
) {
|
||||
try {
|
||||
return await createEndpoint(
|
||||
name,
|
||||
EnvironmentCreationTypes.LocalKubernetesEnvironment,
|
||||
{ tagIds, groupId: 1, tls: { skipClientVerify: true, skipVerify: true } }
|
||||
);
|
||||
} catch (err) {
|
||||
throw new PortainerError('Unable to create environment', err as Error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function createAzureEndpoint(
|
||||
name: string,
|
||||
applicationId: string,
|
||||
tenantId: string,
|
||||
authenticationKey: string,
|
||||
groupId: EnvironmentGroupId,
|
||||
tagIds: TagId[]
|
||||
) {
|
||||
try {
|
||||
await createEndpoint(name, EnvironmentCreationTypes.AzureEnvironment, {
|
||||
groupId,
|
||||
tagIds,
|
||||
azure: { applicationId, tenantId, authenticationKey },
|
||||
});
|
||||
} catch (err) {
|
||||
throw new PortainerError('Unable to connect to Azure', err as Error);
|
||||
}
|
||||
}
|
||||
|
||||
interface TLSSettings {
|
||||
skipVerify?: boolean;
|
||||
skipClientVerify?: boolean;
|
||||
caCertFile?: File;
|
||||
certFile?: File;
|
||||
keyFile?: File;
|
||||
}
|
||||
|
||||
interface AzureSettings {
|
||||
applicationId: string;
|
||||
tenantId: string;
|
||||
authenticationKey: string;
|
||||
}
|
||||
|
||||
interface EndpointOptions {
|
||||
url?: string;
|
||||
publicUrl?: string;
|
||||
groupId?: EnvironmentGroupId;
|
||||
tagIds?: TagId[];
|
||||
checkinInterval?: number;
|
||||
azure?: AzureSettings;
|
||||
tls?: TLSSettings;
|
||||
}
|
||||
|
||||
export async function createRemoteEndpoint(
|
||||
name: string,
|
||||
creationType: EnvironmentCreationTypes,
|
||||
options?: EndpointOptions
|
||||
) {
|
||||
let endpointUrl = options?.url;
|
||||
if (creationType !== EnvironmentCreationTypes.EdgeAgentEnvironment) {
|
||||
endpointUrl = `tcp://${endpointUrl}`;
|
||||
}
|
||||
|
||||
try {
|
||||
return await createEndpoint(name, creationType, {
|
||||
...options,
|
||||
url: endpointUrl,
|
||||
});
|
||||
} catch (err) {
|
||||
throw new PortainerError('Unable to create environment', err as Error);
|
||||
}
|
||||
}
|
||||
|
||||
async function createEndpoint(
|
||||
name: string,
|
||||
creationType: EnvironmentCreationTypes,
|
||||
options?: EndpointOptions
|
||||
) {
|
||||
let payload: Record<string, unknown> = {
|
||||
Name: name,
|
||||
EndpointCreationType: creationType,
|
||||
};
|
||||
|
||||
if (options) {
|
||||
payload = {
|
||||
...payload,
|
||||
URL: options.url,
|
||||
PublicURL: options.publicUrl,
|
||||
GroupID: options.groupId,
|
||||
TagIds: arrayToJson(options.tagIds),
|
||||
CheckinInterval: options.checkinInterval,
|
||||
};
|
||||
|
||||
const { tls, azure } = options;
|
||||
|
||||
if (tls) {
|
||||
payload = {
|
||||
...payload,
|
||||
TLS: true,
|
||||
TLSSkipVerify: tls.skipVerify,
|
||||
TLSSkipClientVerify: tls.skipClientVerify,
|
||||
TLSCACertFile: tls.caCertFile,
|
||||
TLSCertFile: tls.certFile,
|
||||
TLSKeyFile: tls.keyFile,
|
||||
};
|
||||
}
|
||||
|
||||
if (azure) {
|
||||
payload = {
|
||||
...payload,
|
||||
AzureApplicationID: azure.applicationId,
|
||||
AzureTenantID: azure.tenantId,
|
||||
AzureAuthenticationKey: azure.authenticationKey,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const formPayload = json2formData(payload);
|
||||
try {
|
||||
const { data: endpoint } = await axios.post<Environment>(
|
||||
buildUrl(),
|
||||
formPayload
|
||||
);
|
||||
|
||||
return endpoint;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
|
||||
import {
|
||||
Environment,
|
||||
EnvironmentGroupId,
|
||||
EnvironmentId,
|
||||
EnvironmentType,
|
||||
EnvironmentSettings,
|
||||
TagId,
|
||||
TeamId,
|
||||
UserId,
|
||||
} from '../types';
|
||||
|
||||
import { arrayToJson, buildUrl } from './utils';
|
||||
|
||||
interface EndpointsQuery {
|
||||
search?: string;
|
||||
types?: EnvironmentType[];
|
||||
tagIds?: TagId[];
|
||||
endpointIds?: EnvironmentId[];
|
||||
tagsPartialMatch?: boolean;
|
||||
groupId?: EnvironmentGroupId;
|
||||
}
|
||||
|
||||
export async function getEndpoints(
|
||||
start: number,
|
||||
limit: number,
|
||||
{ types, tagIds, endpointIds, ...query }: EndpointsQuery = {}
|
||||
) {
|
||||
if (tagIds && tagIds.length === 0) {
|
||||
return { totalCount: 0, value: <Environment[]>[] };
|
||||
}
|
||||
|
||||
const url = buildUrl();
|
||||
|
||||
const params: Record<string, unknown> = { start, limit, ...query };
|
||||
|
||||
if (types) {
|
||||
params.types = arrayToJson(types);
|
||||
}
|
||||
|
||||
if (tagIds) {
|
||||
params.tagIds = arrayToJson(tagIds);
|
||||
}
|
||||
|
||||
if (endpointIds) {
|
||||
params.endpointIds = arrayToJson(endpointIds);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.get<Environment[]>(url, { params });
|
||||
|
||||
const totalCount = response.headers['X-Total-Count'];
|
||||
|
||||
return { totalCount: parseInt(totalCount, 10), value: response.data };
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getEndpoint(id: EnvironmentId) {
|
||||
try {
|
||||
const { data: endpoint } = await axios.get<Environment>(buildUrl(id));
|
||||
return endpoint;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function snapshotEndpoints() {
|
||||
try {
|
||||
await axios.post<void>(buildUrl(undefined, 'snapshot'));
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function snapshotEndpoint(id: EnvironmentId) {
|
||||
try {
|
||||
await axios.post<void>(buildUrl(id, 'snapshot'));
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function endpointsByGroup(
|
||||
start: number,
|
||||
limit: number,
|
||||
search: string,
|
||||
groupId: EnvironmentGroupId
|
||||
) {
|
||||
return getEndpoints(start, limit, { search, groupId });
|
||||
}
|
||||
|
||||
export async function disassociateEndpoint(id: EnvironmentId) {
|
||||
try {
|
||||
await axios.delete(buildUrl(id, 'association'));
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
interface UpdatePayload {
|
||||
TLSCACert?: File;
|
||||
TLSCert?: File;
|
||||
TLSKey?: File;
|
||||
|
||||
Name: string;
|
||||
PublicURL: string;
|
||||
GroupID: EnvironmentGroupId;
|
||||
TagIds: TagId[];
|
||||
|
||||
EdgeCheckinInterval: number;
|
||||
|
||||
TLS: boolean;
|
||||
TLSSkipVerify: boolean;
|
||||
TLSSkipClientVerify: boolean;
|
||||
AzureApplicationID: string;
|
||||
AzureTenantID: string;
|
||||
AzureAuthenticationKey: string;
|
||||
}
|
||||
|
||||
async function uploadTLSFilesForEndpoint(
|
||||
id: EnvironmentId,
|
||||
tlscaCert?: File,
|
||||
tlsCert?: File,
|
||||
tlsKey?: File
|
||||
) {
|
||||
await Promise.all([
|
||||
uploadCert('ca', tlscaCert),
|
||||
uploadCert('cert', tlsCert),
|
||||
uploadCert('key', tlsKey),
|
||||
]);
|
||||
|
||||
function uploadCert(type: 'ca' | 'cert' | 'key', cert?: File) {
|
||||
if (!cert) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return axios.post<void>(`upload/tls/${type}`, cert, {
|
||||
params: { folder: id },
|
||||
});
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateEndpoint(
|
||||
id: EnvironmentId,
|
||||
payload: UpdatePayload
|
||||
) {
|
||||
try {
|
||||
await uploadTLSFilesForEndpoint(
|
||||
id,
|
||||
payload.TLSCACert,
|
||||
payload.TLSCert,
|
||||
payload.TLSKey
|
||||
);
|
||||
|
||||
const { data: endpoint } = await axios.put<Environment>(
|
||||
buildUrl(id),
|
||||
payload
|
||||
);
|
||||
|
||||
return endpoint;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error, 'Unable to update environment');
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteEndpoint(id: EnvironmentId) {
|
||||
try {
|
||||
await axios.delete(buildUrl(id));
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function updatePoolAccess(
|
||||
id: EnvironmentId,
|
||||
resourcePool: string,
|
||||
usersToAdd: UserId[],
|
||||
teamsToAdd: TeamId[],
|
||||
usersToRemove: UserId[],
|
||||
teamsToRemove: TeamId[]
|
||||
) {
|
||||
try {
|
||||
await axios.put<void>(`${buildUrl(id, 'pools')}/${resourcePool}/access`, {
|
||||
usersToAdd,
|
||||
teamsToAdd,
|
||||
usersToRemove,
|
||||
teamsToRemove,
|
||||
});
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function forceUpdateService(
|
||||
id: EnvironmentId,
|
||||
serviceID: string,
|
||||
pullImage: boolean
|
||||
) {
|
||||
try {
|
||||
await axios.put(buildUrl(id, 'forceupdateservice'), {
|
||||
serviceID,
|
||||
pullImage,
|
||||
});
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateSettings(
|
||||
id: EnvironmentId,
|
||||
settings: EnvironmentSettings
|
||||
) {
|
||||
try {
|
||||
await axios.put(buildUrl(id, 'settings'), settings);
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
|
||||
import {
|
||||
EnvironmentId,
|
||||
TeamAccessPolicies,
|
||||
UserAccessPolicies,
|
||||
} from '../types';
|
||||
|
||||
import { buildUrl } from './utils';
|
||||
|
||||
export type RegistryId = number;
|
||||
export interface Registry {
|
||||
Id: RegistryId;
|
||||
}
|
||||
|
||||
interface RegistryAccess {
|
||||
UserAccessPolicies: UserAccessPolicies;
|
||||
TeamAccessPolicies: TeamAccessPolicies;
|
||||
Namespaces: string[];
|
||||
}
|
||||
|
||||
export async function updateEnvironmentRegistryAccess(
|
||||
id: EnvironmentId,
|
||||
registryId: RegistryId,
|
||||
access: RegistryAccess
|
||||
) {
|
||||
try {
|
||||
await axios.put<void>(buildRegistryUrl(id, registryId), access);
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getEnvironmentRegistries(
|
||||
id: EnvironmentId,
|
||||
namespace: string
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<Registry[]>(buildRegistryUrl(id), {
|
||||
params: { namespace },
|
||||
});
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getEnvironmentRegistry(
|
||||
endpointId: EnvironmentId,
|
||||
registryId: RegistryId
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<Registry>(
|
||||
buildRegistryUrl(endpointId, registryId)
|
||||
);
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error);
|
||||
}
|
||||
}
|
||||
|
||||
function buildRegistryUrl(id: EnvironmentId, registryId?: RegistryId) {
|
||||
let url = `${buildUrl(id)}/registries`;
|
||||
|
||||
if (registryId) {
|
||||
url += `/${registryId}`;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import { EnvironmentId } from '../types';
|
||||
|
||||
export function buildUrl(id?: EnvironmentId, action?: string) {
|
||||
let baseUrl = 'endpoints';
|
||||
if (id) {
|
||||
baseUrl += `/${id}`;
|
||||
}
|
||||
|
||||
if (action) {
|
||||
baseUrl += `/${action}`;
|
||||
}
|
||||
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
export function arrayToJson<T>(arr?: Array<T>) {
|
||||
if (!arr) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return JSON.stringify(arr);
|
||||
}
|
||||
|
||||
export function json2formData(json: Record<string, unknown>) {
|
||||
const formData = new FormData();
|
||||
|
||||
Object.entries(json).forEach(([key, value]) => {
|
||||
if (typeof value === 'undefined' || value === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
formData.append(key, value as string);
|
||||
});
|
||||
|
||||
return formData;
|
||||
}
|
|
@ -1,12 +1,118 @@
|
|||
export type EnvironmentId = number;
|
||||
|
||||
export enum EnvironmentType {
|
||||
// Docker represents an environment(endpoint) connected to a Docker environment(endpoint)
|
||||
Docker = 1,
|
||||
// AgentOnDocker represents an environment(endpoint) connected to a Portainer agent deployed on a Docker environment(endpoint)
|
||||
AgentOnDocker,
|
||||
// Azure represents an environment(endpoint) connected to an Azure environment(endpoint)
|
||||
Azure,
|
||||
// EdgeAgentOnDocker represents an environment(endpoint) connected to an Edge agent deployed on a Docker environment(endpoint)
|
||||
EdgeAgentOnDocker,
|
||||
// KubernetesLocal represents an environment(endpoint) connected to a local Kubernetes environment(endpoint)
|
||||
KubernetesLocal,
|
||||
// AgentOnKubernetes represents an environment(endpoint) connected to a Portainer agent deployed on a Kubernetes environment(endpoint)
|
||||
AgentOnKubernetes,
|
||||
// EdgeAgentOnKubernetes represents an environment(endpoint) connected to an Edge agent deployed on a Kubernetes environment(endpoint)
|
||||
EdgeAgentOnKubernetes,
|
||||
}
|
||||
|
||||
export type TagId = number;
|
||||
|
||||
export interface Tag {
|
||||
Id: TagId;
|
||||
Name: string;
|
||||
}
|
||||
|
||||
export enum EnvironmentStatus {
|
||||
Up = 1,
|
||||
Down = 2,
|
||||
Down,
|
||||
}
|
||||
|
||||
export interface DockerSnapshot {
|
||||
TotalCPU: number;
|
||||
TotalMemory: number;
|
||||
NodeCount: number;
|
||||
ImageCount: number;
|
||||
VolumeCount: number;
|
||||
RunningContainerCount: number;
|
||||
StoppedContainerCount: number;
|
||||
HealthyContainerCount: number;
|
||||
UnhealthyContainerCount: number;
|
||||
Time: number;
|
||||
StackCount: number;
|
||||
ServiceCount: number;
|
||||
Swarm: boolean;
|
||||
DockerVersion: string;
|
||||
}
|
||||
|
||||
export interface KubernetesSnapshot {
|
||||
KubernetesVersion: string;
|
||||
TotalCPU: number;
|
||||
TotalMemory: number;
|
||||
Time: number;
|
||||
NodeCount: number;
|
||||
}
|
||||
|
||||
export interface KubernetesSettings {
|
||||
Snapshots: KubernetesSnapshot[];
|
||||
}
|
||||
|
||||
export interface Environment {
|
||||
Id: EnvironmentId;
|
||||
Type: EnvironmentType;
|
||||
TagIds: TagId[];
|
||||
GroupName: string;
|
||||
EdgeID?: string;
|
||||
EdgeCheckinInterval?: number;
|
||||
LastCheckInDate?: number;
|
||||
Name: string;
|
||||
Status: EnvironmentStatus;
|
||||
PublicURL: string;
|
||||
URL: string;
|
||||
Snapshots: DockerSnapshot[];
|
||||
Kubernetes: KubernetesSettings;
|
||||
PublicURL?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* TS reference of endpoint_create.go#EndpointCreationType iota
|
||||
*/
|
||||
export enum EnvironmentCreationTypes {
|
||||
LocalDockerEnvironment = 1,
|
||||
AgentEnvironment,
|
||||
AzureEnvironment,
|
||||
EdgeAgentEnvironment,
|
||||
LocalKubernetesEnvironment,
|
||||
}
|
||||
|
||||
export type EnvironmentGroupId = number;
|
||||
|
||||
export interface EnvironmentSettings {
|
||||
// Whether non-administrator should be able to use bind mounts when creating containers
|
||||
allowBindMountsForRegularUsers: boolean;
|
||||
// Whether non-administrator should be able to use privileged mode when creating containers
|
||||
allowPrivilegedModeForRegularUsers: boolean;
|
||||
// Whether non-administrator should be able to browse volumes
|
||||
allowVolumeBrowserForRegularUsers: boolean;
|
||||
// Whether non-administrator should be able to use the host pid
|
||||
allowHostNamespaceForRegularUsers: boolean;
|
||||
// Whether non-administrator should be able to use device mapping
|
||||
allowDeviceMappingForRegularUsers: boolean;
|
||||
// Whether non-administrator should be able to manage stacks
|
||||
allowStackManagementForRegularUsers: boolean;
|
||||
// Whether non-administrator should be able to use container capabilities
|
||||
allowContainerCapabilitiesForRegularUsers: boolean;
|
||||
// Whether non-administrator should be able to use sysctl settings
|
||||
allowSysctlSettingForRegularUsers: boolean;
|
||||
// Whether host management features are enabled
|
||||
enableHostManagementFeatures: boolean;
|
||||
}
|
||||
|
||||
export type UserId = number;
|
||||
export type TeamId = number;
|
||||
export type RoleId = number;
|
||||
interface AccessPolicy {
|
||||
RoleId: RoleId;
|
||||
}
|
||||
export type UserAccessPolicies = Record<UserId, AccessPolicy>; // map[UserID]AccessPolicy
|
||||
export type TeamAccessPolicies = Record<TeamId, AccessPolicy>;
|
||||
|
|
Loading…
Reference in New Issue