Wizard: Add masked services input
This exposes masked services in the UI by adding a masked services input.
This commit is contained in:
parent
408ecb2a80
commit
56e85e0954
6 changed files with 105 additions and 20 deletions
|
|
@ -458,7 +458,9 @@ const Review = () => {
|
|||
</ExpandableSection>
|
||||
)}
|
||||
{isServicesStepEnabled &&
|
||||
(services.enabled.length > 0 || services.disabled.length > 0) && (
|
||||
(services.enabled.length > 0 ||
|
||||
services.disabled.length > 0 ||
|
||||
services.masked.length > 0) && (
|
||||
<ExpandableSection
|
||||
toggleContent={composeExpandable(
|
||||
'Systemd services',
|
||||
|
|
|
|||
|
|
@ -991,11 +991,24 @@ export const ServicesList = () => {
|
|||
Disabled
|
||||
</TextListItem>
|
||||
<TextListItem component={TextListItemVariants.dd}>
|
||||
{services.disabled.length > 0 || services.masked.length > 0 ? (
|
||||
{services.disabled.length > 0 ? (
|
||||
<CodeBlock>
|
||||
<CodeBlockCode>
|
||||
{services.disabled.concat(services.masked).join(' ')}
|
||||
</CodeBlockCode>
|
||||
<CodeBlockCode>{services.disabled.join(' ')}</CodeBlockCode>
|
||||
</CodeBlock>
|
||||
) : (
|
||||
'None'
|
||||
)}
|
||||
</TextListItem>
|
||||
<TextListItem
|
||||
component={TextListItemVariants.dt}
|
||||
className="pf-v5-u-min-width"
|
||||
>
|
||||
Masked
|
||||
</TextListItem>
|
||||
<TextListItem component={TextListItemVariants.dd}>
|
||||
{services.masked.length > 0 ? (
|
||||
<CodeBlock>
|
||||
<CodeBlockCode>{services.masked.join(' ')}</CodeBlockCode>
|
||||
</CodeBlock>
|
||||
) : (
|
||||
'None'
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ import { useGetOscapCustomizationsQuery } from '../../../../../store/imageBuilde
|
|||
import {
|
||||
addDisabledService,
|
||||
addEnabledService,
|
||||
addMaskedService,
|
||||
removeDisabledService,
|
||||
removeEnabledService,
|
||||
removeMaskedService,
|
||||
selectComplianceProfileID,
|
||||
selectDistribution,
|
||||
selectServices,
|
||||
|
|
@ -38,13 +40,13 @@ const ServicesInput = () => {
|
|||
}
|
||||
);
|
||||
|
||||
const disabledAndMaskedRequiredByOpenSCAP = disabledServices
|
||||
.concat(maskedServices)
|
||||
.filter(
|
||||
(service) =>
|
||||
oscapProfileInfo?.services?.disabled?.includes(service) ||
|
||||
oscapProfileInfo?.services?.masked?.includes(service)
|
||||
);
|
||||
const disabledRequiredByOpenSCAP = disabledServices.filter((service) =>
|
||||
oscapProfileInfo?.services?.disabled?.includes(service)
|
||||
);
|
||||
|
||||
const maskedRequiredByOpenSCAP = maskedServices.filter((service) =>
|
||||
oscapProfileInfo?.services?.masked?.includes(service)
|
||||
);
|
||||
|
||||
const enabledRequiredByOpenSCAP = enabledServices.filter((service) =>
|
||||
oscapProfileInfo?.services?.enabled?.includes(service)
|
||||
|
|
@ -57,13 +59,11 @@ const ServicesInput = () => {
|
|||
ariaLabel="Add disabled service"
|
||||
placeholder="Add disabled service"
|
||||
validator={isServiceValid}
|
||||
list={disabledServices
|
||||
.concat(maskedServices)
|
||||
.filter(
|
||||
(service) =>
|
||||
!disabledAndMaskedRequiredByOpenSCAP.includes(service)
|
||||
)}
|
||||
requiredList={disabledAndMaskedRequiredByOpenSCAP}
|
||||
list={disabledServices.filter(
|
||||
(service) =>
|
||||
!oscapProfileInfo?.services?.disabled?.includes(service)
|
||||
)}
|
||||
requiredList={disabledRequiredByOpenSCAP}
|
||||
item="Disabled service"
|
||||
addAction={addDisabledService}
|
||||
removeAction={removeDisabledService}
|
||||
|
|
@ -71,6 +71,22 @@ const ServicesInput = () => {
|
|||
fieldName="disabledSystemdServices"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup isRequired={false} label="Masked services">
|
||||
<ChippingInput
|
||||
ariaLabel="Add masked service"
|
||||
placeholder="Add masked service"
|
||||
validator={isServiceValid}
|
||||
list={maskedServices.filter(
|
||||
(service) => !oscapProfileInfo?.services?.masked?.includes(service)
|
||||
)}
|
||||
requiredList={maskedRequiredByOpenSCAP}
|
||||
item="Masked service"
|
||||
addAction={addMaskedService}
|
||||
removeAction={removeMaskedService}
|
||||
stepValidation={stepValidation}
|
||||
fieldName="maskedSystemdServices"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup isRequired={false} label="Enabled services">
|
||||
<ChippingInput
|
||||
ariaLabel="Add enabled service"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const ServicesStep = () => {
|
|||
<Title headingLevel="h1" size="xl">
|
||||
Systemd services
|
||||
</Title>
|
||||
<Text>Enable and disable systemd services.</Text>
|
||||
<Text>Enable, disable and mask systemd services.</Text>
|
||||
<ServicesInput />
|
||||
</Form>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -341,6 +341,7 @@ export function useServicesValidation(): StepValidation {
|
|||
const services = useAppSelector(selectServices);
|
||||
const errors = {};
|
||||
const invalidDisabled = [];
|
||||
const invalidMasked = [];
|
||||
const invalidEnabled = [];
|
||||
|
||||
if (services.disabled.length > 0) {
|
||||
|
|
@ -357,6 +358,20 @@ export function useServicesValidation(): StepValidation {
|
|||
}
|
||||
}
|
||||
|
||||
if (services.masked.length > 0) {
|
||||
for (const s of services.masked) {
|
||||
if (!isServiceValid(s)) {
|
||||
invalidMasked.push(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidMasked.length > 0) {
|
||||
Object.assign(errors, {
|
||||
maskedSystemdServices: `Invalid masked services: ${invalidMasked}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (services.enabled.length > 0) {
|
||||
for (const s of services.enabled) {
|
||||
if (!isServiceValid(s)) {
|
||||
|
|
|
|||
|
|
@ -82,6 +82,14 @@ const addDisabledService = async (service: string) => {
|
|||
await waitFor(() => user.type(disabledServiceInput, service.concat(' ')));
|
||||
};
|
||||
|
||||
const addMaskedService = async (service: string) => {
|
||||
const user = userEvent.setup();
|
||||
const maskedServiceInput = await screen.findByPlaceholderText(
|
||||
'Add masked service'
|
||||
);
|
||||
await waitFor(() => user.type(maskedServiceInput, service.concat(' ')));
|
||||
};
|
||||
|
||||
const addEnabledService = async (service: string) => {
|
||||
const user = userEvent.setup();
|
||||
const enabledServiceInput = await screen.findByPlaceholderText(
|
||||
|
|
@ -157,6 +165,33 @@ describe('Step Services', () => {
|
|||
expect(screen.queryByText('telnet')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('validation works', async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderCreateMode();
|
||||
await goToServicesStep();
|
||||
const clearInputButtons = await screen.findAllByRole('button', {
|
||||
name: /clear input/i,
|
||||
});
|
||||
|
||||
// Disabled services input
|
||||
expect(screen.queryByText('Invalid format.')).not.toBeInTheDocument();
|
||||
await addDisabledService('-------');
|
||||
expect(await screen.findByText('Invalid format.')).toBeInTheDocument();
|
||||
await waitFor(() => user.click(clearInputButtons[0]));
|
||||
|
||||
// Masked services input
|
||||
expect(screen.queryByText('Invalid format.')).not.toBeInTheDocument();
|
||||
await addMaskedService('-------');
|
||||
expect(await screen.findByText('Invalid format.')).toBeInTheDocument();
|
||||
await waitFor(() => user.click(clearInputButtons[1]));
|
||||
|
||||
// Enabled services input
|
||||
expect(screen.queryByText('Invalid format.')).not.toBeInTheDocument();
|
||||
await addEnabledService('-------');
|
||||
expect(await screen.findByText('Invalid format.')).toBeInTheDocument();
|
||||
await waitFor(() => user.click(clearInputButtons[2]));
|
||||
});
|
||||
|
||||
test('services from OpenSCAP get added correctly and cannot be removed', async () => {
|
||||
await renderCreateMode();
|
||||
await goToOpenSCAPStep();
|
||||
|
|
@ -202,6 +237,7 @@ describe('Services request generated correctly', () => {
|
|||
await renderCreateMode();
|
||||
await goToServicesStep();
|
||||
await addDisabledService('telnet');
|
||||
await addMaskedService('nfs-server');
|
||||
await addEnabledService('httpd');
|
||||
await goToReviewStep();
|
||||
// informational modal pops up in the first test only as it's tied
|
||||
|
|
@ -214,6 +250,7 @@ describe('Services request generated correctly', () => {
|
|||
customizations: {
|
||||
services: {
|
||||
disabled: ['telnet'],
|
||||
masked: ['nfs-server'],
|
||||
enabled: ['httpd'],
|
||||
},
|
||||
},
|
||||
|
|
@ -228,8 +265,10 @@ describe('Services request generated correctly', () => {
|
|||
await renderCreateMode();
|
||||
await goToServicesStep();
|
||||
await addDisabledService('telnet');
|
||||
await addMaskedService('nfs-server');
|
||||
await addEnabledService('httpd');
|
||||
await removeService('telnet');
|
||||
await removeService('nfs-server');
|
||||
await removeService('httpd');
|
||||
await goToReviewStep();
|
||||
const receivedRequest = await interceptBlueprintRequest(CREATE_BLUEPRINT);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue