1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-05 13:55:21 +02:00

feat(edge/templates): introduce edge specific settings [EE-6276] (#10609)

This commit is contained in:
Chaim Lev-Ari 2023-11-15 14:43:18 +02:00 committed by GitHub
parent 68950fbb24
commit e43d076269
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 885 additions and 319 deletions

View file

@ -23,7 +23,7 @@ import { EdgeGroup } from '../../edge-groups/types';
import { DeploymentType, EdgeStack } from '../../edge-stacks/types';
import { useEdgeStacks } from '../../edge-stacks/queries/useEdgeStacks';
import { useEdgeGroups } from '../../edge-groups/queries/useEdgeGroups';
import { useCreateEdgeStackFromGit } from '../../edge-stacks/queries/useCreateEdgeStackFromGit';
import { useCreateEdgeStack } from '../../edge-stacks/queries/useCreateEdgeStack/useCreateEdgeStack';
import { EnvVarsFieldset } from './EnvVarsFieldset';
@ -70,7 +70,7 @@ function DeployForm({
unselect: () => void;
}) {
const router = useRouter();
const mutation = useCreateEdgeStackFromGit();
const mutation = useCreateEdgeStack();
const edgeStacksQuery = useEdgeStacks();
const edgeGroupsQuery = useEdgeGroups({
select: (groups) =>
@ -139,15 +139,21 @@ function DeployForm({
function handleSubmit(values: FormValues) {
return mutation.mutate(
{
name: values.name,
edgeGroups: values.edgeGroupIds,
deploymentType: DeploymentType.Compose,
repositoryURL: template.Repository.url,
filePathInRepository: template.Repository.stackfile,
envVars: Object.entries(values.envVars).map(([name, value]) => ({
name,
value,
})),
method: 'git',
payload: {
name: values.name,
edgeGroups: values.edgeGroupIds,
deploymentType: DeploymentType.Compose,
envVars: Object.entries(values.envVars).map(([name, value]) => ({
name,
value,
})),
git: {
RepositoryURL: template.Repository.url,
ComposeFilePathInRepository: template.Repository.stackfile,
},
},
},
{
onSuccess() {

View file

@ -6,6 +6,7 @@ import { notifySuccess } from '@/portainer/services/notifications';
import { useCreateTemplateMutation } from '@/react/portainer/templates/custom-templates/queries/useCreateTemplateMutation';
import { Platform } from '@/react/portainer/templates/types';
import { useFetchTemplateFile } from '@/react/portainer/templates/app-templates/queries/useFetchTemplateFile';
import { getDefaultEdgeTemplateSettings } from '@/react/portainer/templates/custom-templates/types';
import { editor } from '@@/BoxSelector/common-options/build-methods';
@ -47,6 +48,7 @@ export function CreateTemplateForm() {
RepositoryURLValid: true,
TLSSkipVerify: false,
},
EdgeSettings: getDefaultEdgeTemplateSettings(),
};
return (

View file

@ -0,0 +1,81 @@
import { FormikErrors } from 'formik';
import { SetStateAction } from 'react';
import { RelativePathFieldset } from '@/react/portainer/gitops/RelativePathFieldset/RelativePathFieldset';
import { PrivateRegistryFieldsetWrapper } from '@/react/edge/edge-stacks/ItemView/EditEdgeStackForm/PrivateRegistryFieldsetWrapper';
import { PrePullToggle } from '@/react/edge/edge-stacks/components/PrePullToggle';
import { RetryDeployToggle } from '@/react/edge/edge-stacks/components/RetryDeployToggle';
import { EdgeTemplateSettings } from '@/react/portainer/templates/custom-templates/types';
import { GitFormModel } from '@/react/portainer/gitops/types';
import { FormSection } from '@@/form-components/FormSection';
export function EdgeSettingsFieldset({
values,
setValues,
errors,
gitConfig,
fileValues,
setFieldError,
}: {
values: EdgeTemplateSettings;
setValues: (values: SetStateAction<EdgeTemplateSettings>) => void;
errors?: FormikErrors<EdgeTemplateSettings>;
gitConfig?: GitFormModel;
setFieldError: (field: string, message: string) => void;
fileValues: {
fileContent?: string;
file?: File;
};
}) {
const isGit = !!gitConfig;
return (
<>
{isGit && (
<FormSection title="Advanced settings">
<RelativePathFieldset
value={values.RelativePathSettings}
gitModel={gitConfig}
onChange={(newValues) =>
setValues((values) => ({
...values,
RelativePathSettings: {
...values.RelativePathSettings,
...newValues,
},
}))
}
/>
</FormSection>
)}
<PrivateRegistryFieldsetWrapper
value={values.PrivateRegistryId}
onChange={(registryId) =>
setValues((values) => ({
...values,
PrivateRegistryId: registryId,
}))
}
values={fileValues}
onFieldError={(error) => setFieldError('Edge?.Registries', error)}
error={errors?.PrivateRegistryId}
isGit={isGit}
/>
<PrePullToggle
onChange={(value) =>
setValues((values) => ({ ...values, PrePullImage: value }))
}
value={values.PrePullImage}
/>
<RetryDeployToggle
onChange={(value) =>
setValues((values) => ({ ...values, RetryDeploy: value }))
}
value={values.RetryDeploy}
/>
</>
);
}

View file

@ -0,0 +1,19 @@
import { SchemaOf, boolean, mixed, number, object } from 'yup';
import { relativePathValidation } from '@/react/portainer/gitops/RelativePathFieldset/validation';
import { EdgeTemplateSettings } from '@/react/portainer/templates/custom-templates/types';
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
export function edgeFieldsetValidation(): SchemaOf<EdgeTemplateSettings> {
if (!isBE) {
return mixed().default(undefined) as SchemaOf<EdgeTemplateSettings>;
}
return object({
RelativePathSettings: relativePathValidation(),
PrePullImage: boolean().default(false),
RetryDeploy: boolean().default(false),
PrivateRegistryId: number().default(undefined),
StaggerConfig: mixed(),
});
}

View file

@ -1,4 +1,4 @@
import { Form, useFormikContext } from 'formik';
import { Form, FormikErrors, useFormikContext } from 'formik';
import { CommonFields } from '@/react/portainer/custom-templates/components/CommonFields';
import { CustomTemplatesVariablesDefinitionField } from '@/react/portainer/custom-templates/components/CustomTemplatesVariablesDefinitionField';
@ -10,6 +10,8 @@ import {
isTemplateVariablesEnabled,
} from '@/react/portainer/custom-templates/components/utils';
import { TemplateTypeSelector } from '@/react/portainer/custom-templates/components/TemplateTypeSelector';
import { EdgeTemplateSettings } from '@/react/portainer/templates/custom-templates/types';
import { applySetStateAction } from '@/react-tools/apply-set-state-action';
import { BoxSelector } from '@@/BoxSelector';
import { WebEditorForm, usePreventExit } from '@@/WebEditorForm';
@ -23,6 +25,7 @@ import {
} from '@@/BoxSelector/common-options/build-methods';
import { FormValues, Method, buildMethods } from './types';
import { EdgeSettingsFieldset } from './EdgeSettingsFieldset';
export function InnerForm({ isLoading }: { isLoading: boolean }) {
const {
@ -41,6 +44,8 @@ export function InnerForm({ isLoading }: { isLoading: boolean }) {
values.FileContent,
values.Method === editor.value && !isSubmitting
);
const isGit = values.Method === git.value;
return (
<Form className="form-horizontal">
<CommonFields
@ -103,6 +108,15 @@ export function InnerForm({ isLoading }: { isLoading: boolean }) {
/>
)}
{isTemplateVariablesEnabled && (
<CustomTemplatesVariablesDefinitionField
value={values.Variables}
onChange={(values) => setFieldValue('Variables', values)}
isVariablesNamesFromParent={values.Method === editor.value}
errors={errors.Variables}
/>
)}
{values.Method === git.value && (
<GitForm
value={values.Git}
@ -116,12 +130,25 @@ export function InnerForm({ isLoading }: { isLoading: boolean }) {
/>
)}
{isTemplateVariablesEnabled && (
<CustomTemplatesVariablesDefinitionField
value={values.Variables}
onChange={(values) => setFieldValue('Variables', values)}
isVariablesNamesFromParent={values.Method === editor.value}
errors={errors.Variables}
{values.EdgeSettings && (
<EdgeSettingsFieldset
setValues={(edgeSetValues) =>
setValues((values) => ({
...values,
EdgeSettings: applySetStateAction(
edgeSetValues,
values.EdgeSettings
),
}))
}
gitConfig={isGit ? values.Git : undefined}
fileValues={{
fileContent: values.FileContent,
file: values.File,
}}
values={values.EdgeSettings}
errors={errors.EdgeSettings as FormikErrors<EdgeTemplateSettings>}
setFieldError={setFieldError}
/>
)}

View file

@ -3,6 +3,7 @@ import { type Values as CommonFieldsValues } from '@/react/portainer/custom-temp
import { DefinitionFieldValues } from '@/react/portainer/custom-templates/components/CustomTemplatesVariablesDefinitionField';
import { Platform } from '@/react/portainer/templates/types';
import { GitFormModel } from '@/react/portainer/gitops/types';
import { EdgeTemplateSettings } from '@/react/portainer/templates/custom-templates/types';
import {
editor,
@ -22,4 +23,5 @@ export interface FormValues extends CommonFieldsValues {
File: File | undefined;
Git: GitFormModel;
Variables: DefinitionFieldValues;
EdgeSettings?: EdgeTemplateSettings;
}

View file

@ -18,6 +18,7 @@ import {
} from '@@/BoxSelector/common-options/build-methods';
import { buildMethods } from './types';
import { edgeFieldsetValidation } from './EdgeSettingsFieldset.validation';
export function useValidation() {
const { user } = useCurrentUser();
@ -51,6 +52,7 @@ export function useValidation() {
then: () => buildGitValidationSchema(gitCredentialsQuery.data || []),
}),
Variables: variablesValidation(),
EdgeSettings: edgeFieldsetValidation(),
}).concat(
commonFieldsValidation({ templates: customTemplatesQuery.data })
),

View file

@ -38,6 +38,7 @@ export function EditTemplateForm({ template }: { template: CustomTemplate }) {
FileContent: fileQuery.data || '',
Git: template.GitConfig ? toGitFormModel(template.GitConfig) : undefined,
EdgeSettings: template.EdgeSettings,
};
return (
@ -72,6 +73,7 @@ export function EditTemplateForm({ template }: { template: CustomTemplate }) {
Note: values.Note,
Platform: values.Platform,
Variables: values.Variables,
EdgeSettings: values.EdgeSettings,
...values.Git,
},
{

View file

@ -12,9 +12,9 @@ import { EditTemplateForm } from './EditTemplateForm';
export function EditView() {
const router = useRouter();
const {
params: { id },
params: { id: templateId },
} = useCurrentStateAndParams();
const customTemplateQuery = useCustomTemplate(id);
const customTemplateQuery = useCustomTemplate(templateId);
useEffect(() => {
if (customTemplateQuery.data && !customTemplateQuery.data.EdgeTemplate) {

View file

@ -1,4 +1,4 @@
import { Form, useFormikContext } from 'formik';
import { Form, FormikErrors, useFormikContext } from 'formik';
import { RefreshCw } from 'lucide-react';
import { CommonFields } from '@/react/portainer/custom-templates/components/CommonFields';
@ -11,12 +11,16 @@ import {
isTemplateVariablesEnabled,
} from '@/react/portainer/custom-templates/components/utils';
import { TemplateTypeSelector } from '@/react/portainer/custom-templates/components/TemplateTypeSelector';
import { applySetStateAction } from '@/react-tools/apply-set-state-action';
import { EdgeTemplateSettings } from '@/react/portainer/templates/custom-templates/types';
import { WebEditorForm, usePreventExit } from '@@/WebEditorForm';
import { FormActions } from '@@/form-components/FormActions';
import { Button } from '@@/buttons';
import { FormError } from '@@/form-components/FormError';
import { EdgeSettingsFieldset } from '../CreateView/EdgeSettingsFieldset';
import { FormValues } from './types';
export function InnerForm({
@ -74,7 +78,11 @@ export function InnerForm({
value={gitFileContent || values.FileContent}
onChange={handleChangeFileContent}
yaml
placeholder="Define or paste the content of your docker compose file here"
placeholder={
gitFileContent
? 'Preview of the file from git repository'
: 'Define or paste the content of your docker compose file here'
}
error={errors.FileContent}
readonly={isEditorReadonly}
>
@ -91,6 +99,15 @@ export function InnerForm({
</p>
</WebEditorForm>
{isTemplateVariablesEnabled && (
<CustomTemplatesVariablesDefinitionField
value={values.Variables}
onChange={(values) => setFieldValue('Variables', values)}
isVariablesNamesFromParent={!isEditorReadonly}
errors={errors.Variables}
/>
)}
{values.Git && (
<>
<GitForm
@ -121,12 +138,21 @@ export function InnerForm({
</>
)}
{isTemplateVariablesEnabled && (
<CustomTemplatesVariablesDefinitionField
value={values.Variables}
onChange={(values) => setFieldValue('Variables', values)}
isVariablesNamesFromParent={!isEditorReadonly}
errors={errors.Variables}
{values.EdgeSettings && (
<EdgeSettingsFieldset
setValues={(edgeValues) =>
setFieldValue(
'EdgeSettings',
applySetStateAction(edgeValues, values.EdgeSettings)
)
}
gitConfig={values.Git}
fileValues={{
fileContent: values.FileContent,
}}
values={values.EdgeSettings}
errors={errors.EdgeSettings as FormikErrors<EdgeTemplateSettings>}
setFieldError={setFieldError}
/>
)}

View file

@ -3,6 +3,7 @@ import { DefinitionFieldValues } from '@/react/portainer/custom-templates/compon
import { Platform } from '@/react/portainer/templates/types';
import { type Values as CommonFieldsValues } from '@/react/portainer/custom-templates/components/CommonFields';
import { GitFormModel } from '@/react/portainer/gitops/types';
import { EdgeTemplateSettings } from '@/react/portainer/templates/custom-templates/types';
export interface FormValues extends CommonFieldsValues {
Platform: Platform;
@ -10,4 +11,5 @@ export interface FormValues extends CommonFieldsValues {
FileContent: string;
Git?: GitFormModel;
Variables: DefinitionFieldValues;
EdgeSettings?: EdgeTemplateSettings;
}

View file

@ -11,6 +11,8 @@ import { useCurrentUser } from '@/react/hooks/useUser';
import { useCustomTemplates } from '@/react/portainer/templates/custom-templates/queries/useCustomTemplates';
import { Platform } from '@/react/portainer/templates/types';
import { edgeFieldsetValidation } from '../CreateView/EdgeSettingsFieldset.validation';
export function useValidation(
currentTemplateId: CustomTemplate['Id'],
isGit: boolean
@ -40,6 +42,7 @@ export function useValidation(
? buildGitValidationSchema(gitCredentialsQuery.data || [])
: mixed(),
Variables: variablesValidation(),
EdgeSettings: edgeFieldsetValidation(),
}).concat(
commonFieldsValidation({
templates: customTemplatesQuery.data,