mirror of https://github.com/portainer/portainer
fix(gitops): manifest validation warning [EE-6859] (#11664)
parent
a0ab82b866
commit
55667a878a
|
@ -1,7 +1,7 @@
|
||||||
import { IFormController } from 'angular';
|
import { IFormController } from 'angular';
|
||||||
import { FormikErrors } from 'formik';
|
import { FormikErrors } from 'formik';
|
||||||
|
|
||||||
import { GitFormModel } from '@/react/portainer/gitops/types';
|
import { DeployMethod, GitFormModel } from '@/react/portainer/gitops/types';
|
||||||
import { validateGitForm } from '@/react/portainer/gitops/GitForm';
|
import { validateGitForm } from '@/react/portainer/gitops/GitForm';
|
||||||
import { notifyError } from '@/portainer/services/notifications';
|
import { notifyError } from '@/portainer/services/notifications';
|
||||||
import { IAuthenticationService } from '@/portainer/services/types';
|
import { IAuthenticationService } from '@/portainer/services/types';
|
||||||
|
@ -26,6 +26,8 @@ export default class GitFormController {
|
||||||
|
|
||||||
createdFromCustomTemplateId?: number;
|
createdFromCustomTemplateId?: number;
|
||||||
|
|
||||||
|
deployMethod?: DeployMethod;
|
||||||
|
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor(
|
constructor(
|
||||||
$async: <T>(fn: () => Promise<T>) => Promise<T>,
|
$async: <T>(fn: () => Promise<T>) => Promise<T>,
|
||||||
|
@ -67,7 +69,8 @@ export default class GitFormController {
|
||||||
this.errors = await validateGitForm(
|
this.errors = await validateGitForm(
|
||||||
this.gitCredentials,
|
this.gitCredentials,
|
||||||
value,
|
value,
|
||||||
isCreatedFromCustomTemplate
|
isCreatedFromCustomTemplate,
|
||||||
|
this.deployMethod
|
||||||
);
|
);
|
||||||
if (this.errors && Object.keys(this.errors).length > 0) {
|
if (this.errors && Object.keys(this.errors).length > 0) {
|
||||||
this.gitForm?.$setValidity('gitForm', false, this.gitForm);
|
this.gitForm?.$setValidity('gitForm', false, this.gitForm);
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { withUserProvider } from '@/react/test-utils/withUserProvider';
|
||||||
import { GitCredential } from '@/react/portainer/account/git-credentials/types';
|
import { GitCredential } from '@/react/portainer/account/git-credentials/types';
|
||||||
|
|
||||||
import { GitForm, buildGitValidationSchema } from './GitForm';
|
import { GitForm, buildGitValidationSchema } from './GitForm';
|
||||||
import { GitFormModel } from './types';
|
import { DeployMethod, GitFormModel } from './types';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
component: GitForm,
|
component: GitForm,
|
||||||
|
@ -45,7 +45,7 @@ interface Args {
|
||||||
isAdditionalFilesFieldVisible: boolean;
|
isAdditionalFilesFieldVisible: boolean;
|
||||||
isAuthExplanationVisible: boolean;
|
isAuthExplanationVisible: boolean;
|
||||||
isDockerStandalone: boolean;
|
isDockerStandalone: boolean;
|
||||||
deployMethod: 'compose' | 'manifest';
|
deployMethod: DeployMethod;
|
||||||
isForcePullVisible: boolean;
|
isForcePullVisible: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ export function Primary({
|
||||||
return (
|
return (
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
validationSchema={() => buildGitValidationSchema([], false)}
|
validationSchema={() => buildGitValidationSchema([], false, 'compose')}
|
||||||
onSubmit={() => {}}
|
onSubmit={() => {}}
|
||||||
>
|
>
|
||||||
{({ values, errors, setValues }) => (
|
{({ values, errors, setValues }) => (
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { useState } from 'react';
|
||||||
import { ComposePathField } from '@/react/portainer/gitops/ComposePathField';
|
import { ComposePathField } from '@/react/portainer/gitops/ComposePathField';
|
||||||
import { RefField } from '@/react/portainer/gitops/RefField';
|
import { RefField } from '@/react/portainer/gitops/RefField';
|
||||||
import { GitFormUrlField } from '@/react/portainer/gitops/GitFormUrlField';
|
import { GitFormUrlField } from '@/react/portainer/gitops/GitFormUrlField';
|
||||||
import { GitFormModel } from '@/react/portainer/gitops/types';
|
import { DeployMethod, GitFormModel } from '@/react/portainer/gitops/types';
|
||||||
import { TimeWindowDisplay } from '@/react/portainer/gitops/TimeWindowDisplay';
|
import { TimeWindowDisplay } from '@/react/portainer/gitops/TimeWindowDisplay';
|
||||||
|
|
||||||
import { FormSection } from '@@/form-components/FormSection';
|
import { FormSection } from '@@/form-components/FormSection';
|
||||||
|
@ -24,7 +24,7 @@ interface Props {
|
||||||
value: GitFormModel;
|
value: GitFormModel;
|
||||||
onChange: (value: Partial<GitFormModel>) => void;
|
onChange: (value: Partial<GitFormModel>) => void;
|
||||||
environmentType?: 'DOCKER' | 'KUBERNETES' | undefined;
|
environmentType?: 'DOCKER' | 'KUBERNETES' | undefined;
|
||||||
deployMethod?: 'compose' | 'manifest';
|
deployMethod?: DeployMethod;
|
||||||
isDockerStandalone?: boolean;
|
isDockerStandalone?: boolean;
|
||||||
isAdditionalFilesFieldVisible?: boolean;
|
isAdditionalFilesFieldVisible?: boolean;
|
||||||
isForcePullVisible?: boolean;
|
isForcePullVisible?: boolean;
|
||||||
|
@ -142,17 +142,24 @@ export function GitForm({
|
||||||
export async function validateGitForm(
|
export async function validateGitForm(
|
||||||
gitCredentials: Array<GitCredential>,
|
gitCredentials: Array<GitCredential>,
|
||||||
formValues: GitFormModel,
|
formValues: GitFormModel,
|
||||||
isCreatedFromCustomTemplate: boolean
|
isCreatedFromCustomTemplate: boolean,
|
||||||
|
deployMethod: DeployMethod = 'compose'
|
||||||
) {
|
) {
|
||||||
return validateForm<GitFormModel>(
|
return validateForm<GitFormModel>(
|
||||||
() => buildGitValidationSchema(gitCredentials, isCreatedFromCustomTemplate),
|
() =>
|
||||||
|
buildGitValidationSchema(
|
||||||
|
gitCredentials,
|
||||||
|
isCreatedFromCustomTemplate,
|
||||||
|
deployMethod
|
||||||
|
),
|
||||||
formValues
|
formValues
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildGitValidationSchema(
|
export function buildGitValidationSchema(
|
||||||
gitCredentials: Array<GitCredential>,
|
gitCredentials: Array<GitCredential>,
|
||||||
isCreatedFromCustomTemplate: boolean
|
isCreatedFromCustomTemplate: boolean,
|
||||||
|
deployMethod: DeployMethod
|
||||||
): SchemaOf<GitFormModel> {
|
): SchemaOf<GitFormModel> {
|
||||||
return object({
|
return object({
|
||||||
RepositoryURL: string()
|
RepositoryURL: string()
|
||||||
|
@ -171,7 +178,9 @@ export function buildGitValidationSchema(
|
||||||
.required('Repository URL is required'),
|
.required('Repository URL is required'),
|
||||||
RepositoryReferenceName: refFieldValidation(),
|
RepositoryReferenceName: refFieldValidation(),
|
||||||
ComposeFilePathInRepository: string().required(
|
ComposeFilePathInRepository: string().required(
|
||||||
'Compose file path is required'
|
deployMethod === 'compose'
|
||||||
|
? 'Compose file path is required'
|
||||||
|
: 'Manifest file path is required'
|
||||||
),
|
),
|
||||||
AdditionalFiles: array(string().required('Path is required')).default([]),
|
AdditionalFiles: array(string().required('Path is required')).default([]),
|
||||||
RepositoryURLValid: boolean().default(false),
|
RepositoryURLValid: boolean().default(false),
|
||||||
|
|
|
@ -52,6 +52,8 @@ export type GitNewCredentialModel = {
|
||||||
|
|
||||||
export type GitAuthModel = GitCredentialsModel & GitNewCredentialModel;
|
export type GitAuthModel = GitCredentialsModel & GitNewCredentialModel;
|
||||||
|
|
||||||
|
export type DeployMethod = 'compose' | 'manifest';
|
||||||
|
|
||||||
export interface GitFormModel extends GitAuthModel {
|
export interface GitFormModel extends GitAuthModel {
|
||||||
RepositoryURL: string;
|
RepositoryURL: string;
|
||||||
RepositoryURLValid?: boolean;
|
RepositoryURLValid?: boolean;
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||||
import { useEnvironmentDeploymentOptions } from '@/react/portainer/environments/queries/useEnvironment';
|
import { useEnvironmentDeploymentOptions } from '@/react/portainer/environments/queries/useEnvironment';
|
||||||
import { useCurrentEnvironment } from '@/react/hooks/useCurrentEnvironment';
|
import { useCurrentEnvironment } from '@/react/hooks/useCurrentEnvironment';
|
||||||
import { isKubernetesEnvironment } from '@/react/portainer/environments/utils';
|
import { isKubernetesEnvironment } from '@/react/portainer/environments/utils';
|
||||||
|
import { DeployMethod } from '@/react/portainer/gitops/types';
|
||||||
|
|
||||||
import { useInitialValues } from './useInitialValues';
|
import { useInitialValues } from './useInitialValues';
|
||||||
import { FormValues, initialBuildMethods } from './types';
|
import { FormValues, initialBuildMethods } from './types';
|
||||||
|
@ -23,10 +24,12 @@ export function CreateForm({
|
||||||
viewType: 'kube' | 'docker' | 'edge';
|
viewType: 'kube' | 'docker' | 'edge';
|
||||||
defaultType: StackType;
|
defaultType: StackType;
|
||||||
}) {
|
}) {
|
||||||
|
const deployMethod: DeployMethod =
|
||||||
|
defaultType === StackType.Kubernetes ? 'manifest' : 'compose';
|
||||||
const isEdge = !environmentId;
|
const isEdge = !environmentId;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const mutation = useCreateTemplateMutation();
|
const mutation = useCreateTemplateMutation();
|
||||||
const validation = useValidation({ viewType });
|
const validation = useValidation({ viewType, deployMethod });
|
||||||
const buildMethods = useBuildMethods();
|
const buildMethods = useBuildMethods();
|
||||||
|
|
||||||
const initialValues = useInitialValues({
|
const initialValues = useInitialValues({
|
||||||
|
|
|
@ -23,6 +23,10 @@ export function useInitialValues({
|
||||||
|
|
||||||
const { appTemplateId, type = defaultType } = useAppTemplateParams();
|
const { appTemplateId, type = defaultType } = useAppTemplateParams();
|
||||||
|
|
||||||
|
// don't make the file path 'docker-compose.yml' in a kube environment. Keep it empty with the existing 'manifest.yml' placeholder
|
||||||
|
const initialFilePathInRepository =
|
||||||
|
type === StackType.Kubernetes ? '' : 'docker-compose.yml';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
params: { fileContent = '' },
|
params: { fileContent = '' },
|
||||||
} = useCurrentStateAndParams();
|
} = useCurrentStateAndParams();
|
||||||
|
@ -49,7 +53,7 @@ export function useInitialValues({
|
||||||
RepositoryAuthentication: false,
|
RepositoryAuthentication: false,
|
||||||
RepositoryUsername: '',
|
RepositoryUsername: '',
|
||||||
RepositoryPassword: '',
|
RepositoryPassword: '',
|
||||||
ComposeFilePathInRepository: 'docker-compose.yml',
|
ComposeFilePathInRepository: initialFilePathInRepository,
|
||||||
AdditionalFiles: [],
|
AdditionalFiles: [],
|
||||||
RepositoryURLValid: true,
|
RepositoryURLValid: true,
|
||||||
TLSSkipVerify: false,
|
TLSSkipVerify: false,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { useGitCredentials } from '@/react/portainer/account/git-credentials/git
|
||||||
import { useCurrentUser } from '@/react/hooks/useUser';
|
import { useCurrentUser } from '@/react/hooks/useUser';
|
||||||
import { useCustomTemplates } from '@/react/portainer/templates/custom-templates/queries/useCustomTemplates';
|
import { useCustomTemplates } from '@/react/portainer/templates/custom-templates/queries/useCustomTemplates';
|
||||||
import { edgeFieldsetValidation } from '@/react/portainer/templates/custom-templates/CreateView/EdgeSettingsFieldset.validation';
|
import { edgeFieldsetValidation } from '@/react/portainer/templates/custom-templates/CreateView/EdgeSettingsFieldset.validation';
|
||||||
|
import { DeployMethod } from '@/react/portainer/gitops/types';
|
||||||
|
|
||||||
import { file } from '@@/form-components/yup-file-validation';
|
import { file } from '@@/form-components/yup-file-validation';
|
||||||
import {
|
import {
|
||||||
|
@ -22,8 +23,10 @@ import { initialBuildMethods } from './types';
|
||||||
|
|
||||||
export function useValidation({
|
export function useValidation({
|
||||||
viewType,
|
viewType,
|
||||||
|
deployMethod,
|
||||||
}: {
|
}: {
|
||||||
viewType: 'kube' | 'docker' | 'edge';
|
viewType: 'kube' | 'docker' | 'edge';
|
||||||
|
deployMethod: DeployMethod;
|
||||||
}) {
|
}) {
|
||||||
const { user } = useCurrentUser();
|
const { user } = useCurrentUser();
|
||||||
const gitCredentialsQuery = useGitCredentials(user.Id);
|
const gitCredentialsQuery = useGitCredentials(user.Id);
|
||||||
|
@ -58,7 +61,11 @@ export function useValidation({
|
||||||
Git: mixed().when('Method', {
|
Git: mixed().when('Method', {
|
||||||
is: git.value,
|
is: git.value,
|
||||||
then: () =>
|
then: () =>
|
||||||
buildGitValidationSchema(gitCredentialsQuery.data || [], false),
|
buildGitValidationSchema(
|
||||||
|
gitCredentialsQuery.data || [],
|
||||||
|
false,
|
||||||
|
deployMethod
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
Variables: variablesValidation(),
|
Variables: variablesValidation(),
|
||||||
EdgeSettings: viewType === 'edge' ? edgeFieldsetValidation() : mixed(),
|
EdgeSettings: viewType === 'edge' ? edgeFieldsetValidation() : mixed(),
|
||||||
|
@ -68,6 +75,11 @@ export function useValidation({
|
||||||
viewType,
|
viewType,
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
[customTemplatesQuery.data, gitCredentialsQuery.data, viewType]
|
[
|
||||||
|
customTemplatesQuery.data,
|
||||||
|
gitCredentialsQuery.data,
|
||||||
|
viewType,
|
||||||
|
deployMethod,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||||
import { useEnvironmentDeploymentOptions } from '@/react/portainer/environments/queries/useEnvironment';
|
import { useEnvironmentDeploymentOptions } from '@/react/portainer/environments/queries/useEnvironment';
|
||||||
import { useCurrentEnvironment } from '@/react/hooks/useCurrentEnvironment';
|
import { useCurrentEnvironment } from '@/react/hooks/useCurrentEnvironment';
|
||||||
import { isKubernetesEnvironment } from '@/react/portainer/environments/utils';
|
import { isKubernetesEnvironment } from '@/react/portainer/environments/utils';
|
||||||
|
import { DeployMethod } from '@/react/portainer/gitops/types';
|
||||||
|
import { StackType } from '@/react/common/stacks/types';
|
||||||
|
|
||||||
import { CustomTemplate } from '../types';
|
import { CustomTemplate } from '../types';
|
||||||
import { useUpdateTemplateMutation } from '../queries/useUpdateTemplateMutation';
|
import { useUpdateTemplateMutation } from '../queries/useUpdateTemplateMutation';
|
||||||
|
@ -32,10 +34,13 @@ export function EditForm({
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const disableEditor = useDisableEditor(isGit);
|
const disableEditor = useDisableEditor(isGit);
|
||||||
const mutation = useUpdateTemplateMutation();
|
const mutation = useUpdateTemplateMutation();
|
||||||
|
const deployMethod: DeployMethod =
|
||||||
|
template.Type === StackType.Kubernetes ? 'manifest' : 'compose';
|
||||||
const validation = useValidation({
|
const validation = useValidation({
|
||||||
viewType,
|
viewType,
|
||||||
isGit,
|
isGit,
|
||||||
templateId: template.Id,
|
templateId: template.Id,
|
||||||
|
deployMethod,
|
||||||
});
|
});
|
||||||
|
|
||||||
const fileContentQuery = useCustomTemplateFile(template.Id);
|
const fileContentQuery = useCustomTemplateFile(template.Id);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { useGitCredentials } from '@/react/portainer/account/git-credentials/git
|
||||||
import { useCurrentUser } from '@/react/hooks/useUser';
|
import { useCurrentUser } from '@/react/hooks/useUser';
|
||||||
import { useCustomTemplates } from '@/react/portainer/templates/custom-templates/queries/useCustomTemplates';
|
import { useCustomTemplates } from '@/react/portainer/templates/custom-templates/queries/useCustomTemplates';
|
||||||
import { edgeFieldsetValidation } from '@/react/portainer/templates/custom-templates/CreateView/EdgeSettingsFieldset.validation';
|
import { edgeFieldsetValidation } from '@/react/portainer/templates/custom-templates/CreateView/EdgeSettingsFieldset.validation';
|
||||||
|
import { DeployMethod } from '@/react/portainer/gitops/types';
|
||||||
|
|
||||||
import { CustomTemplate } from '../types';
|
import { CustomTemplate } from '../types';
|
||||||
import { TemplateViewType } from '../useViewType';
|
import { TemplateViewType } from '../useViewType';
|
||||||
|
@ -18,10 +19,12 @@ export function useValidation({
|
||||||
isGit,
|
isGit,
|
||||||
templateId,
|
templateId,
|
||||||
viewType,
|
viewType,
|
||||||
|
deployMethod,
|
||||||
}: {
|
}: {
|
||||||
isGit: boolean;
|
isGit: boolean;
|
||||||
templateId: CustomTemplate['Id'];
|
templateId: CustomTemplate['Id'];
|
||||||
viewType: TemplateViewType;
|
viewType: TemplateViewType;
|
||||||
|
deployMethod: DeployMethod;
|
||||||
}) {
|
}) {
|
||||||
const { user } = useCurrentUser();
|
const { user } = useCurrentUser();
|
||||||
const gitCredentialsQuery = useGitCredentials(user.Id);
|
const gitCredentialsQuery = useGitCredentials(user.Id);
|
||||||
|
@ -47,7 +50,11 @@ export function useValidation({
|
||||||
FileContent: string().required('Template is required.'),
|
FileContent: string().required('Template is required.'),
|
||||||
|
|
||||||
Git: isGit
|
Git: isGit
|
||||||
? buildGitValidationSchema(gitCredentialsQuery.data || [], false)
|
? buildGitValidationSchema(
|
||||||
|
gitCredentialsQuery.data || [],
|
||||||
|
false,
|
||||||
|
deployMethod
|
||||||
|
)
|
||||||
: mixed(),
|
: mixed(),
|
||||||
Variables: variablesValidation(),
|
Variables: variablesValidation(),
|
||||||
EdgeSettings: viewType === 'edge' ? edgeFieldsetValidation() : mixed(),
|
EdgeSettings: viewType === 'edge' ? edgeFieldsetValidation() : mixed(),
|
||||||
|
@ -64,6 +71,7 @@ export function useValidation({
|
||||||
isGit,
|
isGit,
|
||||||
templateId,
|
templateId,
|
||||||
viewType,
|
viewType,
|
||||||
|
deployMethod,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue