Skip to content

Commit

Permalink
[WIP] Show only the allowed storage interfaces on Disk-modal
Browse files Browse the repository at this point in the history
Fixes: CNV-2914

 - Enforce the validations from common and user templates
   so the user can choose only from the allowed values in the Disk-modal
   at the storage tab.
 - Add the validations to the store.
 - Implement enum validation methods.

Depends on: openshift#3909

Signed-off-by: Ido Rosenzwig irosenzw@redhat.com
  • Loading branch information
irosenzw committed Jan 15, 2020
1 parent 772c050 commit 1015dc9
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 6 deletions.
@@ -1,3 +1,4 @@
import { TemplateValidations } from 'packages/kubevirt-plugin/src/utils/validations/template/template-validations';
import {
CloudInitField,
VMImportProvider,
Expand Down Expand Up @@ -208,4 +209,11 @@ export const vmWizardInternalActions: VMWizardInternalActions = {
},
type: InternalActionType.SetResults,
}),
[InternalActionType.SetTemplateValidations]: (id, value: TemplateValidations[]) => ({
payload: {
id,
value,
},
type: InternalActionType.SetTemplateValidations,
}),
};
Expand Up @@ -251,6 +251,11 @@ export default (state, action: WizardInternalAction) => {
[dialogID, 'tabs', VMWizardTab.VM_SETTINGS, 'value'],
fromJS(payload.value),
);
case InternalActionType.SetTemplateValidations:
return state.setIn(
[dialogID, 'commonData', 'dataIDReferences', 'templateValidations'],
payload.value,
);
default:
break;
}
Expand Down
@@ -1,4 +1,5 @@
import { FLAGS } from '@console/shared';
import { TemplateValidations } from '../../../../../utils/validations/template/template-validations';
import { isWinToolsImage, getVolumeContainerImage } from '../../../../../selectors/vm';
import {
hasVmSettingsChanged,
Expand All @@ -20,6 +21,7 @@ import { ProvisionSource } from '../../../../../constants/vm/provision-source';
import { getProviders } from '../../../provider-definitions';
import { windowsToolsStorage } from '../../initial-state/storage-tab-initial-state';
import { getStorages } from '../../../selectors/selectors';
import { getTemplateValidations } from '../../validations/utils/templates-validations';
import { prefillVmTemplateUpdater } from './prefill-vm-template-state-update';

export const selectedUserTemplateUpdater = (options: UpdateOptions) => {
Expand Down Expand Up @@ -144,6 +146,13 @@ export const nativeK8sUpdater = ({ id, dispatch, getState, changedCommonData }:
);
};

export const templateValidationsUpdater = (options: UpdateOptions) => {
const validations: TemplateValidations[] = getTemplateValidations(options);
options.dispatch(
vmWizardInternalActions[InternalActionType.SetTemplateValidations](options.id, validations),
);
};

export const updateVmSettingsState = (options: UpdateOptions) =>
[
...(iGetCommonData(options.getState(), options.id, VMWizardProps.isProviderImport)
Expand All @@ -154,6 +163,7 @@ export const updateVmSettingsState = (options: UpdateOptions) =>
flavorUpdater,
osUpdater,
nativeK8sUpdater,
templateValidationsUpdater,
].forEach((updater) => {
updater && updater(options);
});
Expand Up @@ -53,6 +53,7 @@ export enum InternalActionType {
SetNetworks = 'KubevirtVMWizardSetNetworks',
SetStorages = 'KubevirtVMWizardSetStorages',
SetResults = 'KubevirtVMWizardSetResults',
SetTemplateValidations = 'TemplateValidations',
}

export type WizardInternalAction = {
Expand Down
Expand Up @@ -8,6 +8,7 @@ import { getStoragesWithWrappers } from '../../selectors/selectors';
import { iGetStorages } from '../../selectors/immutable/storage';
import { iGetProvisionSource } from '../../selectors/immutable/vm-settings';
import { ProvisionSource } from '../../../../constants/vm/provision-source';
import { getTemplateValidations } from './utils/templates-validations';

export const validateStorages = (options: UpdateOptions) => {
const { id, prevState, dispatch, getState } = options;
Expand Down
Expand Up @@ -35,8 +35,8 @@ export const getTemplateValidations = (options: UpdateOptions): TemplateValidati

if (templates.size > 0 && os && workload) {
// templates are sorted by relevance if only flavor is missing
return getValidationsFromTemplates([templates.first()]);
return getValidationsFromTemplates(templates.toArray());
}

return getValidationsFromTemplates(templates.toArray());
return getValidationsFromTemplates([templates.first()]);
};
@@ -1,5 +1,6 @@
import { get } from 'lodash';
import { Map } from 'immutable';
import { TemplateValidations } from 'packages/kubevirt-plugin/src/utils/validations/template/template-validations';
import { iGetIn, immutableListToShallowJS } from '../../../utils/immutable';
import {
VMWizardNetwork,
Expand Down Expand Up @@ -50,3 +51,6 @@ export const getStoragesWithWrappers = (state, id: string): VMWizardStorageWithW
persistentVolumeClaim,
...rest,
}));

export const getTemplateValidations = (state, id: string): TemplateValidations[] =>
iGetIn(getCreateVMWizards(state), [id, 'commonData', 'dataIDReferences', 'templateValidations']);
Expand Up @@ -8,6 +8,7 @@ import {
ProjectModel,
StorageClassModel,
} from '@console/internal/models';
import { TemplateValidations } from 'packages/kubevirt-plugin/src/utils/validations/template/template-validations';
import { iGetCommonData } from '../../selectors/immutable/selectors';
import {
VMWizardProps,
Expand All @@ -17,7 +18,7 @@ import {
} from '../../types';
import { vmWizardActions } from '../../redux/actions';
import { ActionType } from '../../redux/types';
import { getStoragesWithWrappers } from '../../selectors/selectors';
import { getStoragesWithWrappers, getTemplateValidations } from '../../selectors/selectors';
import { DiskWrapper } from '../../../../k8s/wrapper/vm/disk-wrapper';
import { VolumeWrapper } from '../../../../k8s/wrapper/vm/volume-wrapper';
import { DataVolumeWrapper } from '../../../../k8s/wrapper/vm/data-volume-wrapper';
Expand All @@ -34,6 +35,7 @@ const VMWizardStorageModal: React.FC<VMWizardStorageModalProps> = (props) => {
useProjects,
addUpdateStorage,
storages,
templateValidations,
...restProps
} = props;
const {
Expand Down Expand Up @@ -80,6 +82,18 @@ const VMWizardStorageModal: React.FC<VMWizardStorageModalProps> = (props) => {
},
];

const getAllowedBusses = (): string[] => {
// Empty array means all values are excepted
const allowedBusses: string[] = [];
templateValidations.forEach((validation) => {
validation
.getAllowedBusses()
.forEach((bus) => (!allowedBusses.includes(bus) ? allowedBusses.push(bus) : null));
});

return allowedBusses;
};

return (
<Firehose resources={resources}>
<DiskModal
Expand All @@ -100,6 +114,7 @@ const VMWizardStorageModal: React.FC<VMWizardStorageModalProps> = (props) => {
].includes(type)}
isCreateTemplate={isCreateTemplate}
isEditing={isEditing}
allowedBusses={getAllowedBusses()}
onSubmit={(
resultDiskWrapper,
resultVolumeWrapper,
Expand Down Expand Up @@ -138,6 +153,7 @@ type VMWizardStorageModalProps = ModalComponentProps & {
useProjects?: boolean;
isCreateTemplate: boolean;
storages: VMWizardStorageWithWrappers[];
templateValidations: TemplateValidations[];
addUpdateStorage: (storage: VMWizardStorage) => void;
};

Expand All @@ -148,6 +164,7 @@ const stateToProps = (state, { wizardReduxID }) => {
namespace: iGetCommonData(state, wizardReduxID, VMWizardProps.activeNamespace),
isCreateTemplate: iGetCommonData(state, wizardReduxID, VMWizardProps.isCreateTemplate),
storages: getStoragesWithWrappers(state, wizardReduxID),
templateValidations: getTemplateValidations(state, wizardReduxID),
};
};

Expand Down
Expand Up @@ -69,6 +69,7 @@ export const DiskModal = withHandlePromise((props: DiskModalProps) => {
handlePromise,
close,
cancel,
allowedBusses,
} = props;
const asId = prefixedID.bind(null, 'disk');
const disk = props.disk || DiskWrapper.EMPTY;
Expand Down Expand Up @@ -380,9 +381,24 @@ export const DiskModal = withHandlePromise((props: DiskModalProps) => {
isDisabled={inProgress}
>
<FormSelectPlaceholderOption isDisabled placeholder="--- Select Interface ---" />
{DiskBus.getAll().map((b) => (
<FormSelectOption key={b.getValue()} value={b.getValue()} label={b.toString()} />
))}
{allowedBusses.length === 0
? DiskBus.getAll().map((b) => (
<FormSelectOption
key={b.getValue()}
value={b.getValue()}
label={b.toString()}
/>
))
: DiskBus.getAll().map(
(b) =>
allowedBusses.includes(b.getValue()) && (
<FormSelectOption
key={b.getValue()}
value={b.getValue()}
label={b.toString()}
/>
),
)}
</FormSelect>
</FormRow>
{source.requiresStorageClass() && (
Expand Down Expand Up @@ -443,6 +459,7 @@ export type DiskModalProps = {
vmName: string;
vmNamespace: string;
namespace: string;
allowedBusses: string[];
onNamespaceChanged: (namespace: string) => void;
usedDiskNames: Set<string>;
usedPVCNames: Set<string>;
Expand Down
Expand Up @@ -11,6 +11,7 @@ export class ValidationJSONPath extends ValueEnum<string> {
static readonly MEMORY = new ValidationJSONPath(
'jsonpath::.spec.domain.resources.requests.memory',
);
static readonly BUS = new ValidationJSONPath('jsonpath::.spec.domain.devices.disks[*].disk.bus');
}

export class TemplateValidations {
Expand Down Expand Up @@ -104,6 +105,29 @@ export class TemplateValidations {
return { min, max, isMinInclusive, isMaxInclusive, isValid };
};

getAllowedBusses = (): string[] => this.getAllowedEnumValues(ValidationJSONPath.BUS);

private getAllowedEnumValues = (jsonPath: ValidationJSONPath): string[] => {
// Empty array means all values are allowed
const allowedEnumValues: string[] = [];
const relevantValidations = this.getRelevantValidations(jsonPath);
relevantValidations.forEach((validation) => {
if (!validation.justWarning && 'values' in validation) {
validation.values.forEach((value) =>
!allowedEnumValues.includes(value) ? allowedEnumValues.push(value) : null,
);
}
});
return allowedEnumValues;
};

private validateEnum = (value: string, jsonPath: ValidationJSONPath): boolean => {
const relevantValidations = this.getRelevantValidations(jsonPath);
return relevantValidations.reduce((isPresent, validation) => {
return (validation.justWarning || validation.values.includes(value)) && isPresent;
}, true);
};

private getRelevantValidations = (jsonPath: ValidationJSONPath) =>
this.validations.filter((validation: CommonTemplatesValidation) =>
validation.path.includes(jsonPath.getValue()),
Expand Down

0 comments on commit 1015dc9

Please sign in to comment.