mirror of
https://github.com/portainer/portainer.git
synced 2025-08-04 21:35:23 +02:00
fix(custom-templates): add stack validation, remove custom template validation [EE-7102] (#11938)
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
ci / build_images (map[arch:s390x platform:linux version:]) (push) Has been cancelled
/ triage (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
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
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
ci / build_images (map[arch:s390x platform:linux version:]) (push) Has been cancelled
/ triage (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
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
ci / build_manifests (push) Has been cancelled
Co-authored-by: testa113 <testa113>
This commit is contained in:
parent
0f5988af49
commit
be9d3285e1
15 changed files with 68 additions and 44 deletions
|
@ -14,10 +14,12 @@ export function NameField({
|
|||
onChange,
|
||||
value,
|
||||
errors,
|
||||
placeholder,
|
||||
}: {
|
||||
onChange(value: string): void;
|
||||
value: string;
|
||||
errors?: FormikErrors<string>;
|
||||
placeholder?: string;
|
||||
}) {
|
||||
return (
|
||||
<FormControl inputId="name-input" label="Name" errors={errors} required>
|
||||
|
@ -25,6 +27,7 @@ export function NameField({
|
|||
id="name-input"
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
required
|
||||
data-cy="stack-name-input"
|
||||
/>
|
||||
|
|
|
@ -33,7 +33,7 @@ export const textByType = {
|
|||
(Deployment, Secret, ConfigMap...)
|
||||
</p>
|
||||
<p>
|
||||
You can get more information about Kubernetes file format in the
|
||||
You can get more information about Kubernetes file format in the{' '}
|
||||
<a
|
||||
href="https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/"
|
||||
target="_blank"
|
||||
|
|
|
@ -22,7 +22,7 @@ export function CollapseExpandButton({
|
|||
aria-label={isExpanded ? 'Collapse' : 'Expand'}
|
||||
aria-expanded={isExpanded}
|
||||
type="button"
|
||||
className="flex-none border-none bg-transparent flex items-center p-0 px-3 group"
|
||||
className="flex-none border-none bg-transparent flex items-center p-0 !ml-0 group"
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...props}
|
||||
>
|
||||
|
|
|
@ -6,6 +6,7 @@ import { Link } from '@@/Link';
|
|||
import { TextTip } from '@@/Tip/TextTip';
|
||||
import { Tooltip } from '@@/Tip/Tooltip';
|
||||
import { AutocompleteSelect } from '@@/form-components/AutocompleteSelect';
|
||||
import { FormError } from '@@/form-components/FormError';
|
||||
|
||||
type Props = {
|
||||
stackName: string;
|
||||
|
@ -13,6 +14,7 @@ type Props = {
|
|||
stacks?: string[];
|
||||
inputClassName?: string;
|
||||
textTip?: string;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
export function StackName({
|
||||
|
@ -21,6 +23,7 @@ export function StackName({
|
|||
stacks = [],
|
||||
inputClassName,
|
||||
textTip = "Enter or select a 'stack' name to group multiple deployments together, or else leave empty to ignore.",
|
||||
error = '',
|
||||
}: Props) {
|
||||
const isAdminQuery = useIsEdgeAdmin();
|
||||
const stackResults = useMemo(
|
||||
|
@ -54,9 +57,11 @@ export function StackName({
|
|||
|
||||
return (
|
||||
<>
|
||||
<TextTip className="mb-4" color="blue">
|
||||
{textTip}
|
||||
</TextTip>
|
||||
{textTip ? (
|
||||
<TextTip className="mb-4" color="blue">
|
||||
{textTip}
|
||||
</TextTip>
|
||||
) : null}
|
||||
<div className="form-group">
|
||||
<label
|
||||
htmlFor="stack_name"
|
||||
|
@ -77,6 +82,7 @@ export function StackName({
|
|||
inputId="stack_name"
|
||||
data-cy="k8s-deploy-stack-input"
|
||||
/>
|
||||
{error ? <FormError>{error}</FormError> : null}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
|
4
app/react/kubernetes/DeployView/StackName/constants.ts
Normal file
4
app/react/kubernetes/DeployView/StackName/constants.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
// this regex is to satisfy k8s label validation rules
|
||||
// alphanumeric, lowercase, uppercase, can contain dashes, dots and underscores, max 63 characters
|
||||
export const KUBE_STACK_NAME_VALIDATION_REGEX =
|
||||
/^(([a-zA-Z0-9](?:(?:[-a-zA-Z0-9_.]){0,61}[a-zA-Z0-9])?))$/;
|
|
@ -95,14 +95,10 @@ export function CommonFields({
|
|||
export function validation({
|
||||
currentTemplateId,
|
||||
templates = [],
|
||||
viewType = 'docker',
|
||||
}: {
|
||||
currentTemplateId?: CustomTemplate['Id'];
|
||||
templates?: Array<CustomTemplate>;
|
||||
viewType?: 'kube' | 'docker' | 'edge';
|
||||
} = {}): SchemaOf<Values> {
|
||||
const titlePattern = titlePatternValidation(viewType);
|
||||
|
||||
return object({
|
||||
Title: string()
|
||||
.required('Title is required.')
|
||||
|
@ -116,31 +112,12 @@ export function validation({
|
|||
template.Title === value && template.Id !== currentTemplateId
|
||||
)
|
||||
)
|
||||
.matches(titlePattern.pattern, titlePattern.error),
|
||||
.max(
|
||||
200,
|
||||
'Custom template title must be less than or equal to 200 characters'
|
||||
),
|
||||
Description: string().required('Description is required.'),
|
||||
Note: string().default(''),
|
||||
Logo: string().default(''),
|
||||
});
|
||||
}
|
||||
|
||||
export const TEMPLATE_NAME_VALIDATION_REGEX = '^[-_a-z0-9]+$';
|
||||
|
||||
const KUBE_TEMPLATE_NAME_VALIDATION_REGEX =
|
||||
'^(([a-z0-9](?:(?:[-a-z0-9_.]){0,61}[a-z0-9])?))$'; // alphanumeric, lowercase, can contain dashes, dots and underscores, max 63 characters
|
||||
|
||||
function titlePatternValidation(type: 'kube' | 'docker' | 'edge') {
|
||||
switch (type) {
|
||||
case 'kube':
|
||||
return {
|
||||
pattern: new RegExp(KUBE_TEMPLATE_NAME_VALIDATION_REGEX),
|
||||
error:
|
||||
"This field must consist of lower-case alphanumeric characters, '.', '_' or '-', must start and end with an alphanumeric character and must be 63 characters or less (e.g. 'my-name', or 'abc-123').",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
pattern: new RegExp(TEMPLATE_NAME_VALIDATION_REGEX),
|
||||
error:
|
||||
"This field must consist of lower-case alphanumeric characters, '_' or '-' (e.g. 'my-name', or 'abc-123').",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import { MetadataFieldset } from './MetadataFieldset';
|
|||
|
||||
export function MoreSettingsSection({ children }: PropsWithChildren<unknown>) {
|
||||
return (
|
||||
<FormSection title="More settings" isFoldable>
|
||||
<FormSection title="More settings" className="ml-0" isFoldable>
|
||||
<div className="ml-8">
|
||||
{children}
|
||||
|
||||
|
|
|
@ -72,7 +72,6 @@ export function useValidation({
|
|||
}).concat(
|
||||
commonFieldsValidation({
|
||||
templates: customTemplatesQuery.data,
|
||||
viewType,
|
||||
})
|
||||
),
|
||||
[
|
||||
|
|
|
@ -62,7 +62,6 @@ export function useValidation({
|
|||
commonFieldsValidation({
|
||||
templates: customTemplatesQuery.data,
|
||||
currentTemplateId: templateId,
|
||||
viewType,
|
||||
})
|
||||
),
|
||||
[
|
||||
|
|
|
@ -63,7 +63,7 @@ export function DeployForm({
|
|||
const isGit = !!template.GitConfig;
|
||||
|
||||
const initialValues: FormValues = {
|
||||
name: template.Title || '',
|
||||
name: '',
|
||||
variables: getVariablesFieldDefaultValues(template.Variables),
|
||||
accessControl: parseAccessControlFormData(
|
||||
isEdgeAdminQuery.isAdmin,
|
||||
|
@ -86,6 +86,7 @@ export function DeployForm({
|
|||
value={values.name}
|
||||
onChange={(v) => setFieldValue('name', v)}
|
||||
errors={errors.name}
|
||||
placeholder="e.g. mystack"
|
||||
/>
|
||||
</FormSection>
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue