portainer/app/react/common/stacks/queries/useCreateStack/useCreateStack.ts

303 lines
9.8 KiB
TypeScript

import { useMutation, useQueryClient } from '@tanstack/react-query';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { Pair } from '@/react/portainer/settings/types';
import {
GitFormModel,
RelativePathModel,
} from '@/react/portainer/gitops/types';
import { applyResourceControl } from '@/react/portainer/access-control/access-control.service';
import { AccessControlFormData } from '@/react/portainer/access-control/types';
import PortainerError from '@/portainer/error';
import { withError, withInvalidate } from '@/react-tools/react-query';
import { queryKeys } from '../query-keys';
import { createSwarmStackFromFile } from './createSwarmStackFromFile';
import { createSwarmStackFromGit } from './createSwarmStackFromGit';
import { createSwarmStackFromFileContent } from './createSwarmStackFromFileContent';
import { createStandaloneStackFromFile } from './createStandaloneStackFromFile';
import { createStandaloneStackFromGit } from './createStandaloneStackFromGit';
import { createStandaloneStackFromFileContent } from './createStandaloneStackFromFileContent';
import { createKubernetesStackFromUrl } from './createKubernetesStackFromUrl';
import { createKubernetesStackFromGit } from './createKubernetesStackFromGit';
import { createKubernetesStackFromFileContent } from './createKubernetesStackFromFileContent';
export function useCreateStack() {
const queryClient = useQueryClient();
return useMutation(createStack, {
...withError('Failed to create stack'),
...withInvalidate(queryClient, [queryKeys.base()]),
});
}
type BasePayload = {
name: string;
environmentId: EnvironmentId;
};
type DockerBasePayload = BasePayload & {
env?: Array<Pair>;
accessControl: AccessControlFormData;
};
type SwarmBasePayload = DockerBasePayload & {
swarmId: string;
};
type KubernetesBasePayload = BasePayload & {
namespace: string;
composeFormat: boolean;
};
export type SwarmCreatePayload =
| {
method: 'file';
payload: SwarmBasePayload & {
/** File to upload */
file: File;
/** Optional webhook configuration */
webhook?: string;
};
}
| {
method: 'string';
payload: SwarmBasePayload & {
/** Content of the Stack file */
fileContent: string;
/** Optional webhook configuration */
webhook?: string;
fromAppTemplate?: boolean;
};
}
| {
method: 'git';
payload: SwarmBasePayload & {
git: GitFormModel;
relativePathSettings?: RelativePathModel;
fromAppTemplate?: boolean;
};
};
type StandaloneCreatePayload =
| {
method: 'file';
payload: DockerBasePayload & {
/** File to upload */
file: File;
/** Optional webhook configuration */
webhook?: string;
};
}
| {
method: 'string';
payload: DockerBasePayload & {
/** Content of the Stack file */
fileContent: string;
/** Optional webhook configuration */
webhook?: string;
fromAppTemplate?: boolean;
};
}
| {
method: 'git';
payload: DockerBasePayload & {
git: GitFormModel;
relativePathSettings?: RelativePathModel;
fromAppTemplate?: boolean;
};
};
type KubernetesCreatePayload =
| {
method: 'string';
payload: KubernetesBasePayload & {
/** Content of the Stack file */
fileContent: string;
/** Optional webhook configuration */
webhook?: string;
};
}
| {
method: 'git';
payload: KubernetesBasePayload & {
git: GitFormModel;
relativePathSettings?: RelativePathModel;
};
}
| {
method: 'url';
payload: KubernetesBasePayload & {
manifestUrl: string;
};
};
export type CreateStackPayload =
| ({ type: 'swarm' } & SwarmCreatePayload)
| ({ type: 'standalone' } & StandaloneCreatePayload)
| ({ type: 'kubernetes' } & KubernetesCreatePayload);
async function createStack(payload: CreateStackPayload) {
const stack = await createActualStack(payload);
if (payload.type === 'standalone' || payload.type === 'swarm') {
const resourceControl = stack.ResourceControl;
// Portainer will always return a resource control, but since types mark it as optional, we need to check it.
// Ignoring the missing value will result with bugs, hence it's better to throw an error
if (!resourceControl) {
throw new PortainerError('resource control expected after creation');
}
await applyResourceControl(
payload.payload.accessControl,
resourceControl.Id
);
}
}
function createActualStack(payload: CreateStackPayload) {
switch (payload.type) {
case 'swarm':
return createSwarmStack(payload);
case 'standalone':
return createStandaloneStack(payload);
case 'kubernetes':
return createKubernetesStack(payload);
default:
throw new Error('Invalid type');
}
}
function createSwarmStack({ method, payload }: SwarmCreatePayload) {
switch (method) {
case 'file':
return createSwarmStackFromFile({
environmentId: payload.environmentId,
file: payload.file,
Name: payload.name,
SwarmID: payload.swarmId,
Env: payload.env,
Webhook: payload.webhook,
});
case 'git':
return createSwarmStackFromGit({
name: payload.name,
env: payload.env,
repositoryUrl: payload.git.RepositoryURL,
repositoryReferenceName: payload.git.RepositoryReferenceName,
composeFile: payload.git.ComposeFilePathInRepository,
repositoryAuthentication: payload.git.RepositoryAuthentication,
repositoryUsername: payload.git.RepositoryUsername,
repositoryPassword: payload.git.RepositoryPassword,
repositoryGitCredentialId: payload.git.RepositoryGitCredentialID,
filesystemPath: payload.relativePathSettings?.FilesystemPath,
supportRelativePath: payload.relativePathSettings?.SupportRelativePath,
tlsSkipVerify: payload.git.TLSSkipVerify,
autoUpdate: payload.git.AutoUpdate,
environmentId: payload.environmentId,
swarmID: payload.swarmId,
additionalFiles: payload.git.AdditionalFiles,
fromAppTemplate: payload.fromAppTemplate,
});
case 'string':
return createSwarmStackFromFileContent({
name: payload.name,
env: payload.env,
environmentId: payload.environmentId,
stackFileContent: payload.fileContent,
webhook: payload.webhook,
swarmID: payload.swarmId,
fromAppTemplate: payload.fromAppTemplate,
});
default:
throw new Error('Invalid method');
}
}
function createStandaloneStack({ method, payload }: StandaloneCreatePayload) {
switch (method) {
case 'file':
return createStandaloneStackFromFile({
environmentId: payload.environmentId,
file: payload.file,
Name: payload.name,
Env: payload.env,
Webhook: payload.webhook,
});
case 'git':
return createStandaloneStackFromGit({
name: payload.name,
env: payload.env,
repositoryUrl: payload.git.RepositoryURL,
repositoryReferenceName: payload.git.RepositoryReferenceName,
composeFile: payload.git.ComposeFilePathInRepository,
repositoryAuthentication: payload.git.RepositoryAuthentication,
repositoryUsername: payload.git.RepositoryUsername,
repositoryPassword: payload.git.RepositoryPassword,
repositoryGitCredentialId: payload.git.RepositoryGitCredentialID,
filesystemPath: payload.relativePathSettings?.FilesystemPath,
supportRelativePath: payload.relativePathSettings?.SupportRelativePath,
tlsSkipVerify: payload.git.TLSSkipVerify,
autoUpdate: payload.git.AutoUpdate,
environmentId: payload.environmentId,
additionalFiles: payload.git.AdditionalFiles,
fromAppTemplate: payload.fromAppTemplate,
});
case 'string':
return createStandaloneStackFromFileContent({
name: payload.name,
env: payload.env,
environmentId: payload.environmentId,
stackFileContent: payload.fileContent,
webhook: payload.webhook,
fromAppTemplate: payload.fromAppTemplate,
});
default:
throw new Error('Invalid method');
}
}
function createKubernetesStack({ method, payload }: KubernetesCreatePayload) {
switch (method) {
case 'string':
return createKubernetesStackFromFileContent({
stackName: payload.name,
environmentId: payload.environmentId,
stackFileContent: payload.fileContent,
composeFormat: payload.composeFormat,
namespace: payload.namespace,
});
case 'git':
return createKubernetesStackFromGit({
stackName: payload.name,
repositoryUrl: payload.git.RepositoryURL,
repositoryReferenceName: payload.git.RepositoryReferenceName,
manifestFile: payload.git.ComposeFilePathInRepository,
repositoryAuthentication: payload.git.RepositoryAuthentication,
repositoryUsername: payload.git.RepositoryUsername,
repositoryPassword: payload.git.RepositoryPassword,
repositoryGitCredentialId: payload.git.RepositoryGitCredentialID,
tlsSkipVerify: payload.git.TLSSkipVerify,
autoUpdate: payload.git.AutoUpdate,
environmentId: payload.environmentId,
additionalFiles: payload.git.AdditionalFiles,
composeFormat: payload.composeFormat,
namespace: payload.namespace,
});
case 'url':
return createKubernetesStackFromUrl({
stackName: payload.name,
composeFormat: payload.composeFormat,
environmentId: payload.environmentId,
manifestURL: payload.manifestUrl,
namespace: payload.namespace,
});
default:
throw new Error('Invalid method');
}
}