diff --git a/src/Components/CreateImageWizard/CreateImageWizard.tsx b/src/Components/CreateImageWizard/CreateImageWizard.tsx
index 13295bbc..0fd25fce 100644
--- a/src/Components/CreateImageWizard/CreateImageWizard.tsx
+++ b/src/Components/CreateImageWizard/CreateImageWizard.tsx
@@ -40,6 +40,7 @@ import {
useDetailsValidation,
useRegistrationValidation,
useHostnameValidation,
+ useKernelValidation,
} from './utilities/useValidation';
import {
isAwsAccountIdValid,
@@ -223,6 +224,8 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => {
const fileSystemValidation = useFilesystemValidation();
// Hostname
const hostnameValidation = useHostnameValidation();
+ // Kernel
+ const kernelValidation = useKernelValidation();
// Firstboot
const firstBootValidation = useFirstBootValidation();
// Details
@@ -510,8 +513,12 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => {
key="wizard-kernel"
navItem={customStatusNavItem}
isHidden={!isKernelEnabled}
+ status={kernelValidation.disabledNext ? 'error' : 'default'}
footer={
-
+
}
>
diff --git a/src/Components/CreateImageWizard/steps/Kernel/components/KernelName.tsx b/src/Components/CreateImageWizard/steps/Kernel/components/KernelName.tsx
index d8492b2f..e9ab4899 100644
--- a/src/Components/CreateImageWizard/steps/Kernel/components/KernelName.tsx
+++ b/src/Components/CreateImageWizard/steps/Kernel/components/KernelName.tsx
@@ -4,6 +4,8 @@ import { FormGroup } from '@patternfly/react-core';
import {
Alert,
Button,
+ HelperText,
+ HelperTextItem,
MenuToggle,
MenuToggleElement,
Select,
@@ -20,6 +22,7 @@ import {
changeKernelName,
selectKernel,
} from '../../../../../store/wizardSlice';
+import { useKernelValidation } from '../../../utilities/useValidation';
const initialOptions = ['kernel', 'kernel-debug'];
let kernelOptions = initialOptions;
@@ -28,6 +31,8 @@ const KernelName = () => {
const dispatch = useAppDispatch();
const kernel = useAppSelector(selectKernel).name;
+ const stepValidation = useKernelValidation();
+
const [isOpen, setIsOpen] = useState(false);
const [inputValue, setInputValue] = useState('');
const [filterValue, setFilterValue] = useState('');
@@ -166,6 +171,13 @@ const KernelName = () => {
))}
+ {stepValidation.errors.kernel && (
+
+
+ {stepValidation.errors.kernel}
+
+
+ )}
>
);
diff --git a/src/Components/CreateImageWizard/utilities/useValidation.tsx b/src/Components/CreateImageWizard/utilities/useValidation.tsx
index 2307c6d2..d8c3f8a0 100644
--- a/src/Components/CreateImageWizard/utilities/useValidation.tsx
+++ b/src/Components/CreateImageWizard/utilities/useValidation.tsx
@@ -19,6 +19,7 @@ import {
selectActivationKey,
selectRegistrationType,
selectHostname,
+ selectKernel,
} from '../../../store/wizardSlice';
import {
getDuplicateMountPoints,
@@ -27,6 +28,7 @@ import {
isMountpointMinSizeValid,
isSnapshotValid,
isHostnameValid,
+ isKernelNameValid,
} from '../validators';
export type StepValidation = {
@@ -41,6 +43,7 @@ export function useIsBlueprintValid(): boolean {
const filesystem = useFilesystemValidation();
const snapshot = useSnapshotValidation();
const hostname = useHostnameValidation();
+ const kernel = useKernelValidation();
const firstBoot = useFirstBootValidation();
const details = useDetailsValidation();
return (
@@ -48,6 +51,7 @@ export function useIsBlueprintValid(): boolean {
!filesystem.disabledNext &&
!snapshot.disabledNext &&
!hostname.disabledNext &&
+ !kernel.disabledNext &&
!firstBoot.disabledNext &&
!details.disabledNext
);
@@ -155,6 +159,20 @@ export function useHostnameValidation(): StepValidation {
return { errors: {}, disabledNext: false };
}
+export function useKernelValidation(): StepValidation {
+ const kernel = useAppSelector(selectKernel);
+
+ if (!isKernelNameValid(kernel.name)) {
+ return {
+ errors: {
+ kernel: 'Invalid format.',
+ },
+ disabledNext: true,
+ };
+ }
+ return { errors: {}, disabledNext: false };
+}
+
export function useDetailsValidation(): StepValidation {
const name = useAppSelector(selectBlueprintName);
const description = useAppSelector(selectBlueprintDescription);
diff --git a/src/Components/CreateImageWizard/validators.ts b/src/Components/CreateImageWizard/validators.ts
index 8fcbf84b..05e6a2f8 100644
--- a/src/Components/CreateImageWizard/validators.ts
+++ b/src/Components/CreateImageWizard/validators.ts
@@ -91,6 +91,17 @@ export const isHostnameValid = (hostname: string) => {
);
};
+export const isKernelNameValid = (kernelName: string) => {
+ if (!kernelName) {
+ return true;
+ }
+
+ return (
+ kernelName.length < 65 &&
+ /^[a-z0-9]|[a-z0-9][a-z0-9-_.+]*[a-z0-9]$/.test(kernelName)
+ );
+};
+
export const isPortValid = (port: string) => {
return /^(\d{1,5}|[a-z]{1,6})(-\d{1,5})?:[a-z]{1,6}$/.test(port);
};
diff --git a/src/test/Components/CreateImageWizard/steps/Kernel/Kernel.test.tsx b/src/test/Components/CreateImageWizard/steps/Kernel/Kernel.test.tsx
index 19398e9f..9af2bfe9 100644
--- a/src/test/Components/CreateImageWizard/steps/Kernel/Kernel.test.tsx
+++ b/src/test/Components/CreateImageWizard/steps/Kernel/Kernel.test.tsx
@@ -8,6 +8,7 @@ import {
clickBack,
clickNext,
enterBlueprintName,
+ getNextButton,
interceptBlueprintRequest,
openAndDismissSaveAndBuildModal,
verifyCancelButton,
@@ -121,6 +122,20 @@ describe('Step Kernel', () => {
await openKernelNameOptions(kernelNameDropdown);
await screen.findByText(CUSTOM_NAME);
});
+
+ test('disable Next with invalid custom kernel name', async () => {
+ await renderCreateMode();
+ await goToKernelStep();
+
+ await selectCustomKernelName('-----------');
+ await screen.findByText(/Invalid format/);
+ const nextButton = await getNextButton();
+ expect(nextButton).toBeDisabled();
+
+ await clearKernelName();
+ expect(screen.queryByText(/Invalid format/)).not.toBeInTheDocument();
+ expect(nextButton).toBeEnabled();
+ });
});
describe('Kernel request generated correctly', () => {