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 21, 2025
1 parent 8dd30ca commit bbaf932
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 21 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
4 changes: 4 additions & 0 deletions src/utils/resources/vm/utils/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { MAX_K8S_NAME_LENGTH } from '@kubevirt-utils/utils/constants';

export const validateVMNameLength = (vmName: string): boolean =>
vmName.length <= MAX_K8S_NAME_LENGTH;
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import useKubevirtUserSettings from '@kubevirt-utils/hooks/useKubevirtUserSettin
import useRHELAutomaticSubscription from '@kubevirt-utils/hooks/useRHELAutomaticSubscription/useRHELAutomaticSubscription';
import { getResourceUrl } from '@kubevirt-utils/resources/shared';
import useNamespaceUDN from '@kubevirt-utils/resources/udn/hooks/useNamespaceUDN';
import { validateVMNameLength } from '@kubevirt-utils/resources/vm/utils/helpers';
import { vmSignal } from '@kubevirt-utils/store/customizeInstanceType';
import { createHeadlessService } from '@kubevirt-utils/utils/headless-service';
import { isEmpty } from '@kubevirt-utils/utils/utils';
Expand Down Expand Up @@ -204,7 +205,11 @@ const CreateVMFooter: FC = () => {
};

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

return (
<footer className="create-vm-instance-type-footer">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import {
UseInstanceTypeAndPreferencesValues,
} from '@catalog/CreateFromInstanceTypes/state/utils/types';
import FolderSelect from '@kubevirt-utils/components/FolderSelect/FolderSelect';
import FormGroupHelperText from '@kubevirt-utils/components/FormGroupHelperText/FormGroupHelperText';
import VirtualMachineDescriptionItem from '@kubevirt-utils/components/VirtualMachineDescriptionItem/VirtualMachineDescriptionItem';
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';
import { convertResourceArrayToMap } from '@kubevirt-utils/resources/shared';
import { validateVMNameLength } from '@kubevirt-utils/resources/vm/utils/helpers';
import { MAX_K8S_NAME_LENGTH } from '@kubevirt-utils/utils/constants';
import { isEmpty } from '@kubevirt-utils/utils/utils';
import { DescriptionList, TextInput } from '@patternfly/react-core';
import { DescriptionList, TextInput, ValidatedOptions } from '@patternfly/react-core';

import { getCPUAndMemoryFromDefaultInstanceType, getOSFromDefaultPreference } from '../utils/utils';

Expand All @@ -24,6 +27,10 @@ const DetailsLeftGrid: FC<DetailsLeftGridProps> = ({ instanceTypesAndPreferences
const { t } = useKubevirtTranslation();
const { featureEnabled: treeViewEnabled } = useFeatures(TREE_VIEW);
const { featureEnabled: treeViewFoldersEnabled } = useFeatures(TREE_VIEW_FOLDERS);
// const [vmNameValidated, setVMNameValidated] = useState<ValidatedOptions>(
// ValidatedOptions.default,
// );

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

// useEffect(() => {
// setVMNameValidated(
// validateVMNameLength(vmName) ? ValidatedOptions.default : ValidatedOptions.error,
// );
// }, [setVMNameValidated, vmName]);

const vmNameValidated = validateVMNameLength(vmName)
? ValidatedOptions.default
: ValidatedOptions.error;

const operatingSystem = getOSFromDefaultPreference(selectedBootableVolume, preferencesMap);
const cpuMemoryString = !isEmpty(instanceTypesMap?.[selectedInstanceType?.name])
? getCPUAndMemoryFromDefaultInstanceType(instanceTypesMap[selectedInstanceType?.name])
Expand All @@ -44,17 +61,29 @@ 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}
/>
<FormGroupHelperText validated={vmNameValidated}>
{vmNameValidated === ValidatedOptions.error &&
t('Maximum name length is {{ maxNameLength }} characters', {
maxNameLength: MAX_K8S_NAME_LENGTH,
})}
</FormGroupHelperText>
</>
}
descriptionHeader={t('Name')}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { FC, memo } from 'react';

import { DRAWER_FORM_ID } from '@catalog/templatescatalog/utils/consts';
import FolderSelect from '@kubevirt-utils/components/FolderSelect/FolderSelect';
import FormGroupHelperText from '@kubevirt-utils/components/FormGroupHelperText/FormGroupHelperText';
import VirtualMachineDescriptionItem from '@kubevirt-utils/components/VirtualMachineDescriptionItem/VirtualMachineDescriptionItem';
import {
RUNSTRATEGY_ALWAYS,
Expand All @@ -11,6 +12,8 @@ import { TREE_VIEW, TREE_VIEW_FOLDERS } from '@kubevirt-utils/hooks/useFeatures/
import { useFeatures } from '@kubevirt-utils/hooks/useFeatures/useFeatures';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { RHELAutomaticSubscriptionData } from '@kubevirt-utils/hooks/useRHELAutomaticSubscription/utils/types';
import { validateVMNameLength } from '@kubevirt-utils/resources/vm/utils/helpers';
import { MAX_K8S_NAME_LENGTH } from '@kubevirt-utils/utils/constants';
import {
Alert,
AlertVariant,
Expand All @@ -25,6 +28,7 @@ import {
StackItem,
TextInput,
Tooltip,
ValidatedOptions,
} from '@patternfly/react-core';

import useCreateDrawerForm from './hooks/useCreateDrawerForm';
Expand Down Expand Up @@ -64,6 +68,10 @@ export const TemplatesCatalogDrawerCreateForm: FC<TemplatesCatalogDrawerCreateFo
startVM,
} = useCreateDrawerForm(namespace, subscriptionData, authorizedSSHKey);

const vmNameValidated = validateVMNameLength(nameField)
? ValidatedOptions.default
: ValidatedOptions.error;

const error = templateLoadingError || createError;
return (
<form className="template-catalog-drawer-form" id="quick-create-form">
Expand All @@ -84,6 +92,12 @@ export const TemplatesCatalogDrawerCreateForm: FC<TemplatesCatalogDrawerCreateFo
value={nameField}
/>
</FormGroup>
<FormGroupHelperText validated={vmNameValidated}>
{vmNameValidated === ValidatedOptions.error &&
t('Maximum name length is {{ maxNameLength }} characters', {
maxNameLength: MAX_K8S_NAME_LENGTH,
})}
</FormGroupHelperText>
</SplitItem>
{treeViewEnabled && treeViewFoldersEnabled && (
<SplitItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
DEFAULT_NETWORK_INTERFACE,
UDN_BINDING_NAME,
} from '@kubevirt-utils/resources/vm/utils/constants';
import { validateVMNameLength } from '@kubevirt-utils/resources/vm/utils/helpers';
import {
HEADLESS_SERVICE_LABEL,
HEADLESS_SERVICE_NAME,
Expand Down Expand Up @@ -98,6 +99,7 @@ const useCreateDrawerForm = (
const { password, username } = registryCredentials;

const { nameField, onVMNameChange } = useCreateVMName();
const isValidVMName = validateVMNameLength(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
@@ -1,12 +1,15 @@
import React, { useMemo, useState } from 'react';
import produce from 'immer';

import {
getHelperTextMessage,
isValidVMName,
} from '@catalog/wizard/tabs/overview/components/VMNameModal/utils/utils';
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 { 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,7 +29,7 @@ const VMNameModal: React.FC<HostnameModalProps> = ({ isOpen, onClose, onSubmit,
return updatedVM;
}, [vm, vmName]);

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

return (
<TabModal
Expand All @@ -45,9 +48,7 @@ const VMNameModal: React.FC<HostnameModalProps> = ({ isOpen, onClose, onSubmit,
value={vmName}
/>
<FormGroupHelperText validated={validated}>
{validated === ValidatedOptions.error
? t('VirtualMachine name can not be empty.')
: t('Please provide name to VirtualMachine.')}
{getHelperTextMessage(vmName)}
</FormGroupHelperText>
</FormGroup>
</Form>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { t } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { validateVMNameLength } from '@kubevirt-utils/resources/vm/utils/helpers';
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 isValidVMName = (vmName: string) => {
const nameMissing = isEmpty(vmName);
const nameTooLong = !validateVMNameLength(vmName);

return nameMissing || nameTooLong ? ValidatedOptions.error : ValidatedOptions.default;
};

export const getHelperTextMessage = (vmName: string): string => {
const nameMissing = isEmpty(vmName);
const nameTooLong = !validateVMNameLength(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 t('Please provide name to VirtualMachine.');
};

0 comments on commit bbaf932

Please sign in to comment.