V2 Wizard: Fix OpenSCAP step
OpenSCAP profile info is now correctly loaded into the state and verified to be in the final request with tests. I modified the fixtures for the OpenSCAP profiles. My changes ensure we have a Venn-diagram like overlap when changing from one profile to another where one package is the same, one package is removed, and one package is added. The same is true for the services and partitions. For kernel args it is not so important as that is just a string (as opposed to an array), so it is enough to be different. I was able to eliminate a useEffect by replacing it with a lazy query trigger function. Setting the second arg `preferCacheValue` to `true` means that a request is only made if cached data is not available. I modified the Redux reducers a bit to add some additional safety. `changeFileSystemPartitionMode` is now responsible for initializing the partitions field in the state by adding the root partition – previously this was done in the components themselves by dispatching `addPartition`. This reducer can always be safely dispatched – if the mode is ‘manual’, dispatching it with a payload of ‘manual’ will not result in any changes to the state. `addPackage` is also safer now. When a package is added, the list of packages is checked. If there is a package with an identical name, the new package overwrites the previous package. This is useful because the description may be different for the same package – for instance, when adding an OpenSCAP package, we use a custom description (‘this package required by OpenSCAP’).
This commit is contained in:
parent
f9fcbf8cdf
commit
a1bdcaa54f
10 changed files with 374 additions and 102 deletions
|
|
@ -1,20 +1,23 @@
|
|||
import React from 'react';
|
||||
|
||||
import { FormGroup, Label, Radio } from '@patternfly/react-core';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { UNIT_GIB } from '../../../../constants';
|
||||
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
|
||||
import {
|
||||
changeFileSystemConfiguration,
|
||||
changeFileSystemPartitionMode,
|
||||
selectFileSystemPartitionMode,
|
||||
selectProfile,
|
||||
} from '../../../../store/wizardSlice';
|
||||
|
||||
const FileSystemPartition = () => {
|
||||
const id = uuidv4();
|
||||
const dispatch = useAppDispatch();
|
||||
const fileSystemPartitionMode = useAppSelector(selectFileSystemPartitionMode);
|
||||
const hasOscapProfile = useAppSelector(selectProfile);
|
||||
|
||||
if (hasOscapProfile) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return (
|
||||
<FormGroup>
|
||||
<Radio
|
||||
|
|
@ -33,7 +36,6 @@ const FileSystemPartition = () => {
|
|||
isChecked={fileSystemPartitionMode === 'automatic'}
|
||||
onChange={() => {
|
||||
dispatch(changeFileSystemPartitionMode('automatic'));
|
||||
dispatch(changeFileSystemConfiguration([]));
|
||||
}}
|
||||
/>
|
||||
<Radio
|
||||
|
|
@ -45,16 +47,6 @@ const FileSystemPartition = () => {
|
|||
isChecked={fileSystemPartitionMode === 'manual'}
|
||||
onChange={() => {
|
||||
dispatch(changeFileSystemPartitionMode('manual'));
|
||||
dispatch(
|
||||
changeFileSystemConfiguration([
|
||||
{
|
||||
id: id,
|
||||
mountpoint: '/',
|
||||
min_size: (10 * UNIT_GIB).toString(),
|
||||
unit: 'GiB',
|
||||
},
|
||||
])
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import {
|
||||
Alert,
|
||||
|
|
@ -14,30 +14,38 @@ import {
|
|||
SelectVariant,
|
||||
} from '@patternfly/react-core/deprecated';
|
||||
import { HelpIcon } from '@patternfly/react-icons';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import OscapProfileInformation from './OscapProfileInformation';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
|
||||
import {
|
||||
useAppDispatch,
|
||||
useAppSelector,
|
||||
useServerStore,
|
||||
} from '../../../../store/hooks';
|
||||
import {
|
||||
DistributionProfileItem,
|
||||
Filesystem,
|
||||
useGetOscapCustomizationsQuery,
|
||||
useGetOscapProfilesQuery,
|
||||
useLazyGetOscapCustomizationsQuery,
|
||||
} from '../../../../store/imageBuilderApi';
|
||||
import {
|
||||
changeOscapProfile,
|
||||
selectDistribution,
|
||||
selectProfile,
|
||||
clearOscapPackages,
|
||||
addPackage,
|
||||
selectPackages,
|
||||
addPartition,
|
||||
changeFileSystemPartitionMode,
|
||||
removePackage,
|
||||
clearPartitions,
|
||||
} from '../../../../store/wizardSlice';
|
||||
import { Partition } from '../FileSystem/FileSystemConfiguration';
|
||||
|
||||
const ProfileSelector = () => {
|
||||
const oscapProfile = useAppSelector(selectProfile);
|
||||
|
||||
const oscapData = useServerStore();
|
||||
const release = useAppSelector(selectDistribution);
|
||||
const packages = useAppSelector(selectPackages);
|
||||
const dispatch = useAppDispatch();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const {
|
||||
|
|
@ -50,38 +58,9 @@ const ProfileSelector = () => {
|
|||
distribution: release,
|
||||
});
|
||||
|
||||
const { data: oscapData } = useGetOscapCustomizationsQuery(
|
||||
{
|
||||
distribution: release,
|
||||
// @ts-ignore if oscapProfile is undefined the query is going to get skipped, so it's safe here to ignore the linter here
|
||||
profile: oscapProfile,
|
||||
},
|
||||
{
|
||||
skip: !oscapProfile,
|
||||
}
|
||||
);
|
||||
const profileName = oscapProfile ? oscapData?.openscap?.profile_name : 'None';
|
||||
const profileName = oscapProfile ? oscapData.profileName : 'None';
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(clearOscapPackages());
|
||||
for (const pkg in oscapData?.packages) {
|
||||
if (
|
||||
packages
|
||||
.map((pkg) => pkg.name)
|
||||
.includes(oscapData?.packages[Number(pkg)])
|
||||
) {
|
||||
dispatch(removePackage(oscapData?.packages[Number(pkg)]));
|
||||
}
|
||||
dispatch(
|
||||
addPackage({
|
||||
name: oscapData?.packages[Number(pkg)],
|
||||
summary: 'Required by chosen OpenSCAP profile',
|
||||
repository: 'distro',
|
||||
isRequiredByOpenScap: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
}, [oscapData?.packages, dispatch]);
|
||||
const [trigger] = useLazyGetOscapCustomizationsQuery();
|
||||
|
||||
const handleToggle = () => {
|
||||
if (!isOpen) {
|
||||
|
|
@ -92,14 +71,79 @@ const ProfileSelector = () => {
|
|||
|
||||
const handleClear = () => {
|
||||
dispatch(changeOscapProfile(undefined));
|
||||
dispatch(clearOscapPackages());
|
||||
clearOscapPackages(oscapData.packages || []);
|
||||
dispatch(changeFileSystemPartitionMode('automatic'));
|
||||
};
|
||||
|
||||
const handlePackages = (
|
||||
oldOscapPackages: string[],
|
||||
newOscapPackages: string[]
|
||||
) => {
|
||||
clearOscapPackages(oldOscapPackages);
|
||||
|
||||
for (const pkg of newOscapPackages) {
|
||||
dispatch(
|
||||
addPackage({
|
||||
name: pkg,
|
||||
summary: 'Required by chosen OpenSCAP profile',
|
||||
repository: 'distro',
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const clearOscapPackages = (oscapPackages: string[]) => {
|
||||
for (const pkg of oscapPackages) {
|
||||
dispatch(removePackage(pkg));
|
||||
}
|
||||
};
|
||||
|
||||
const handlePartitions = (oscapPartitions: Filesystem[]) => {
|
||||
dispatch(clearPartitions());
|
||||
|
||||
const newPartitions = oscapPartitions.map((filesystem) => {
|
||||
const partition: Partition = {
|
||||
mountpoint: filesystem.mountpoint,
|
||||
min_size: filesystem.min_size.toString(),
|
||||
unit: 'GiB',
|
||||
id: uuidv4(),
|
||||
};
|
||||
return partition;
|
||||
});
|
||||
|
||||
if (newPartitions) {
|
||||
dispatch(changeFileSystemPartitionMode('manual'));
|
||||
for (const partition of newPartitions) {
|
||||
dispatch(addPartition(partition));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelect = (
|
||||
_event: React.MouseEvent<Element, MouseEvent>,
|
||||
selection: OScapSelectOptionValueType
|
||||
) => {
|
||||
dispatch(changeOscapProfile(selection.id));
|
||||
if (selection.id === undefined) {
|
||||
// handle user has selected 'None' case
|
||||
handleClear();
|
||||
} else {
|
||||
const oldOscapPackages = oscapData.packages || [];
|
||||
trigger(
|
||||
{
|
||||
distribution: release,
|
||||
profile: selection.id,
|
||||
},
|
||||
true // preferCacheValue
|
||||
)
|
||||
.unwrap()
|
||||
.then((response) => {
|
||||
const oscapPartitions = response.filesystem || [];
|
||||
const newOscapPackages = response.packages || [];
|
||||
handlePartitions(oscapPartitions);
|
||||
handlePackages(oldOscapPackages, newOscapPackages);
|
||||
dispatch(changeOscapProfile(selection.id));
|
||||
});
|
||||
}
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ export type IBPackageWithRepositoryInfo = {
|
|||
name: Package['name'];
|
||||
summary: Package['summary'];
|
||||
repository: PackageRepository;
|
||||
isRequiredByOpenScap: boolean;
|
||||
};
|
||||
|
||||
const Packages = () => {
|
||||
|
|
@ -386,7 +385,6 @@ const Packages = () => {
|
|||
transformedDistroData = dataDistroPackages.data.map((values) => ({
|
||||
...values,
|
||||
repository: 'distro',
|
||||
isRequiredByOpenScap: false,
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
@ -395,7 +393,6 @@ const Packages = () => {
|
|||
name: values.package_name!,
|
||||
summary: values.summary!,
|
||||
repository: 'custom',
|
||||
isRequiredByOpenScap: false,
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
@ -413,7 +410,6 @@ const Packages = () => {
|
|||
name: values.package_name!,
|
||||
summary: values.summary!,
|
||||
repository: 'recommended',
|
||||
isRequiredByOpenScap: false,
|
||||
}));
|
||||
|
||||
combinedPackageData = combinedPackageData.concat(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue