Skip to content

Commit

Permalink
Limit VM name length to max k8s label length
Browse files Browse the repository at this point in the history
  • Loading branch information
pcbailey committed Jan 22, 2025
1 parent 8dd30ca commit 2231014
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 24 deletions.
1 change: 1 addition & 0 deletions locales/en/plugin__kubevirt-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,7 @@
"Max. running migrations per cluster": "Max. running migrations per cluster",
"Max. running migrations per node": "Max. running migrations per node",
"Maximum latency": "Maximum latency",
"Maximum name length is {{ maxNameLength }} characters": "Maximum name length is {{ maxNameLength }} characters",
"Measurement duration": "Measurement duration",
"Mediated devices": "Mediated devices",
"medium": "medium",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { FC } from 'react';

import FormGroupHelperText from '@kubevirt-utils/components/FormGroupHelperText/FormGroupHelperText';
import {
getVMNameValidationMessage,
validateVMName,
} from '@kubevirt-utils/components/VMNameValidationHelperText/utils/utils';

type VMNameValidationHelperTextProps = {
showDefaultHelperText?: boolean;
vmName: string;
};

const VMNameValidationHelperText: FC<VMNameValidationHelperTextProps> = ({
showDefaultHelperText = false,
vmName,
}) => {
const vmNameValidated = validateVMName(vmName);
const vmNameValidationMessage = getVMNameValidationMessage(vmName, showDefaultHelperText);

return (
<FormGroupHelperText validated={vmNameValidated}>{vmNameValidationMessage}</FormGroupHelperText>
);
};

export default VMNameValidationHelperText;
30 changes: 30 additions & 0 deletions src/utils/components/VMNameValidationHelperText/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { t } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { MAX_K8S_NAME_LENGTH } from '@kubevirt-utils/utils/constants';
import { isEmpty } from '@kubevirt-utils/utils/utils';
import { ValidatedOptions } from '@patternfly/react-core';

export const vmNameLengthExceedsMaxLength = (vmName: string) =>
vmName?.length > MAX_K8S_NAME_LENGTH;

export const isValidVMName = (vmName: string): boolean => {
const nameMissing = isEmpty(vmName);
const nameTooLong = vmNameLengthExceedsMaxLength(vmName);

return !nameMissing && !nameTooLong;
};

export const validateVMName = (vmName: string): ValidatedOptions =>
isValidVMName(vmName) ? ValidatedOptions.default : ValidatedOptions.error;

export const getVMNameValidationMessage = (vmName: string, showDefaultMessage: boolean): string => {
const nameMissing = isEmpty(vmName);
const nameTooLong = vmNameLengthExceedsMaxLength(vmName);

if (nameMissing) return t('VirtualMachine name can not be empty.');
if (nameTooLong)
return t('Maximum name length is {{ maxNameLength }} characters', {
maxNameLength: MAX_K8S_NAME_LENGTH,
});

return showDefaultMessage ? t('Please provide name to VirtualMachine.') : '';
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
generateNewSysprepConfig,
UNATTEND,
} from '@kubevirt-utils/components/SysprepModal/sysprep-utils';
import { isValidVMName } from '@kubevirt-utils/components/VMNameValidationHelperText/utils/utils';
import { DEFAULT_NAMESPACE } from '@kubevirt-utils/constants/constants';
import { VirtualMachineDetailsTab } from '@kubevirt-utils/constants/tabs-constants';
import { logITFlowEvent } from '@kubevirt-utils/extensions/telemetry/telemetry';
Expand Down Expand Up @@ -203,8 +204,14 @@ const CreateVMFooter: FC = () => {
}
};

const isValidVmName = isValidVMName(vmName);

const isDisabled =
isSubmitting || isEmpty(selectedBootableVolume) || !canCreateVM || !hasNameAndInstanceType;
isSubmitting ||
isEmpty(selectedBootableVolume) ||
!canCreateVM ||
!hasNameAndInstanceType ||
!isValidVmName;

return (
<footer className="create-vm-instance-type-footer">
Expand Down Expand Up @@ -249,7 +256,8 @@ const CreateVMFooter: FC = () => {
isSubmitting ||
isEmpty(selectedBootableVolume) ||
!canCreateVM ||
!hasNameAndInstanceType
!hasNameAndInstanceType ||
!isValidVmName
}
isLoading={isSubmitting}
onClick={handleCustomize}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
} from '@catalog/CreateFromInstanceTypes/state/utils/types';
import FolderSelect from '@kubevirt-utils/components/FolderSelect/FolderSelect';
import VirtualMachineDescriptionItem from '@kubevirt-utils/components/VirtualMachineDescriptionItem/VirtualMachineDescriptionItem';
import { validateVMName } from '@kubevirt-utils/components/VMNameValidationHelperText/utils/utils';
import VMNameValidationHelperText from '@kubevirt-utils/components/VMNameValidationHelperText/VMNameValidationHelperText';
import { TREE_VIEW, TREE_VIEW_FOLDERS } from '@kubevirt-utils/hooks/useFeatures/constants';
import { useFeatures } from '@kubevirt-utils/hooks/useFeatures/useFeatures';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
Expand All @@ -24,6 +26,7 @@ const DetailsLeftGrid: FC<DetailsLeftGridProps> = ({ instanceTypesAndPreferences
const { t } = useKubevirtTranslation();
const { featureEnabled: treeViewEnabled } = useFeatures(TREE_VIEW);
const { featureEnabled: treeViewFoldersEnabled } = useFeatures(TREE_VIEW_FOLDERS);

const { instanceTypeVMState, setInstanceTypeVMState, vmNamespaceTarget } =
useInstanceTypeVMStore();
const { folder, selectedBootableVolume, selectedInstanceType, vmName } = instanceTypeVMState;
Expand All @@ -35,6 +38,8 @@ const DetailsLeftGrid: FC<DetailsLeftGridProps> = ({ instanceTypesAndPreferences
[clusterInstanceTypes],
);

const vmNameValidated = validateVMName(vmName);

const operatingSystem = getOSFromDefaultPreference(selectedBootableVolume, preferencesMap);
const cpuMemoryString = !isEmpty(instanceTypesMap?.[selectedInstanceType?.name])
? getCPUAndMemoryFromDefaultInstanceType(instanceTypesMap[selectedInstanceType?.name])
Expand All @@ -44,17 +49,24 @@ const DetailsLeftGrid: FC<DetailsLeftGridProps> = ({ instanceTypesAndPreferences
<DescriptionList className="pf-c-description-list" isHorizontal>
<VirtualMachineDescriptionItem
descriptionData={
<TextInput
onChange={(_event, newVMName) =>
setInstanceTypeVMState({ payload: newVMName, type: instanceTypeActionType.setVMName })
}
aria-label="instancetypes virtualmachine name"
data-test-id="instancetypes-vm-name-input"
isRequired
name="vmname"
type="text"
value={vmName}
/>
<>
<TextInput
onChange={(_event, newVMName) =>
setInstanceTypeVMState({
payload: newVMName,
type: instanceTypeActionType.setVMName,
})
}
aria-label="instancetypes virtualmachine name"
data-test-id="instancetypes-vm-name-input"
isRequired
name="vmname"
type="text"
validated={vmNameValidated}
value={vmName}
/>
<VMNameValidationHelperText vmName={vmName} />
</>
}
descriptionHeader={t('Name')}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React, { FC, memo } from 'react';
import { DRAWER_FORM_ID } from '@catalog/templatescatalog/utils/consts';
import FolderSelect from '@kubevirt-utils/components/FolderSelect/FolderSelect';
import VirtualMachineDescriptionItem from '@kubevirt-utils/components/VirtualMachineDescriptionItem/VirtualMachineDescriptionItem';
import { validateVMName } from '@kubevirt-utils/components/VMNameValidationHelperText/utils/utils';
import VMNameValidationHelperText from '@kubevirt-utils/components/VMNameValidationHelperText/VMNameValidationHelperText';
import {
RUNSTRATEGY_ALWAYS,
RUNSTRATEGY_RERUNONFAILURE,
Expand Down Expand Up @@ -64,6 +66,8 @@ export const TemplatesCatalogDrawerCreateForm: FC<TemplatesCatalogDrawerCreateFo
startVM,
} = useCreateDrawerForm(namespace, subscriptionData, authorizedSSHKey);

const vmNameValidated = validateVMName(nameField);

const error = templateLoadingError || createError;
return (
<form className="template-catalog-drawer-form" id="quick-create-form">
Expand All @@ -81,9 +85,11 @@ export const TemplatesCatalogDrawerCreateForm: FC<TemplatesCatalogDrawerCreateFo
name="vmname"
onChange={(_, value: string) => onVMNameChange(value)}
type="text"
validated={vmNameValidated}
value={nameField}
/>
</FormGroup>
<VMNameValidationHelperText vmName={nameField} />
</SplitItem>
{treeViewEnabled && treeViewFoldersEnabled && (
<SplitItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
applyCloudDriveCloudInitVolume,
createSSHSecret,
} from '@kubevirt-utils/components/SSHSecretModal/utils/utils';
import { isValidVMName } from '@kubevirt-utils/components/VMNameValidationHelperText/utils/utils';
import {
RUNSTRATEGY_ALWAYS,
RUNSTRATEGY_HALTED,
Expand Down Expand Up @@ -98,6 +99,7 @@ const useCreateDrawerForm = (
const { password, username } = registryCredentials;

const { nameField, onVMNameChange } = useCreateVMName();
const isValidVmName = isValidVMName(nameField);

const [isQuickCreating, setIsQuickCreating] = useState(false);
const [isCustomizing, setIsCustomizing] = useState(false);
Expand Down Expand Up @@ -331,7 +333,7 @@ const useCreateDrawerForm = (
return {
createError,
folder: getLabel(vm, VM_FOLDER_LABEL),
isCustomizeDisabled: !processedTemplateAccessReview || isCustomizing,
isCustomizeDisabled: !processedTemplateAccessReview || isCustomizing || !isValidVmName,
isCustomizeLoading: isCustomizing || modelsLoading,
isQuickCreateDisabled:
!isBootSourceAvailable ||
Expand All @@ -340,7 +342,8 @@ const useCreateDrawerForm = (
isEmpty(models) ||
!allRequiredParametersAreFulfilled(template) ||
!hasValidSource(template) ||
storageClassRequiredMissing,
storageClassRequiredMissing ||
!isValidVmName,
isQuickCreateLoading: isQuickCreating || modelsLoading,
nameField,
onChangeFolder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import React, { useMemo, useState } from 'react';
import produce from 'immer';

import { V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt';
import FormGroupHelperText from '@kubevirt-utils/components/FormGroupHelperText/FormGroupHelperText';
import TabModal from '@kubevirt-utils/components/TabModal/TabModal';
import {
isValidVMName,
validateVMName,
} from '@kubevirt-utils/components/VMNameValidationHelperText/utils/utils';
import VMNameValidationHelperText from '@kubevirt-utils/components/VMNameValidationHelperText/VMNameValidationHelperText';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { isEmpty } from '@kubevirt-utils/utils/utils';
import { Form, FormGroup, TextInput, ValidatedOptions } from '@patternfly/react-core';
import { Form, FormGroup, TextInput } from '@patternfly/react-core';

type HostnameModalProps = {
isOpen: boolean;
Expand All @@ -26,11 +29,12 @@ const VMNameModal: React.FC<HostnameModalProps> = ({ isOpen, onClose, onSubmit,
return updatedVM;
}, [vm, vmName]);

const validated = !isEmpty(vmName) ? ValidatedOptions.default : ValidatedOptions.error;
const vmNameValidated = validateVMName(vmName);

return (
<TabModal
headerText={t('Edit VirtualMachine name')}
isDisabled={!isValidVMName(vmName)}
isOpen={isOpen}
obj={updatedVirtualMachine}
onClose={onClose}
Expand All @@ -42,13 +46,10 @@ const VMNameModal: React.FC<HostnameModalProps> = ({ isOpen, onClose, onSubmit,
id="vm-name"
onChange={(_event, val) => setVMName(val)}
type="text"
validated={vmNameValidated}
value={vmName}
/>
<FormGroupHelperText validated={validated}>
{validated === ValidatedOptions.error
? t('VirtualMachine name can not be empty.')
: t('Please provide name to VirtualMachine.')}
</FormGroupHelperText>
<VMNameValidationHelperText showDefaultHelperText vmName={vmName} />
</FormGroup>
</Form>
</TabModal>
Expand Down

0 comments on commit 2231014

Please sign in to comment.