Skip to content
This repository has been archived by the owner on Apr 28, 2020. It is now read-only.

Commit

Permalink
CloneDialog: support non-admin users (#521)
Browse files Browse the repository at this point in the history
* CloneDialog: support non-admin users

* CloneDialog: show worst loadError
  • Loading branch information
suomiy authored and mareklibra committed Jul 22, 2019
1 parent f98b9bc commit 195dfbc
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 28 deletions.
138 changes: 118 additions & 20 deletions src/components/Dialog/CloneDialog/CloneDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import {
START_VM_KEY,
VIRTUAL_MACHINES_KEY,
} from '../../Wizard/CreateVmWizard/constants';
import { getDescription, getNamespace, getName, isVmRunning } from '../../../selectors';
import { getDescription, getNamespace, getName, isVmRunning, getVolumes } from '../../../selectors';
import { validateVmName, vmAlreadyExists } from '../../../utils/validations';
import { settingsValue } from '../../../k8s/selectors';
import { clone } from '../../../k8s/clone';
import { getResource } from '../../../utils/utils';
import { Loading } from '../../Loading';
import { DataVolumeModel, NamespaceModel, PersistentVolumeClaimModel, VirtualMachineModel } from '../../../models';

const getFormFields = (namespaces, vm, persistentVolumeClaims, dataVolumes, virtualMachines) => ({
[NAME_KEY]: {
Expand Down Expand Up @@ -54,11 +56,18 @@ const getFormFields = (namespaces, vm, persistentVolumeClaims, dataVolumes, virt
},
});

const getLoadedData = (result, defaultValue) =>
result && result.loaded && !result.loadError ? result.data : defaultValue;

export class CloneDialog extends React.Component {
constructor(props) {
super(props);
const initVmName = `${getName(props.vm)}-clone`;
const initVmNameValidation = vmAlreadyExists(initVmName, getNamespace(props.vm), props.virtualMachines);
const initVmNameValidation = vmAlreadyExists(
initVmName,
getNamespace(props.vm),
getLoadedData(props.virtualMachines, [])
);
if (initVmNameValidation && initVmNameValidation.message) {
initVmNameValidation.message = `Name ${initVmNameValidation.message}`;
}
Expand Down Expand Up @@ -114,10 +123,10 @@ export class CloneDialog extends React.Component {
settingsValue(this.state, NAMESPACE_KEY),
settingsValue(this.state, DESCRIPTION_KEY),
settingsValue(this.state, START_VM_KEY),
this.props.persistentVolumeClaims,
this.props.dataVolumes
getLoadedData(this.props.persistentVolumeClaims, []),
getLoadedData(this.props.dataVolumes, [])
)
.then(() => this.props.onClose())
.then(() => this.props.close())
.catch(error =>
this.setState({
cloning: false,
Expand All @@ -129,36 +138,58 @@ export class CloneDialog extends React.Component {
onErrorDismissed = () => this.setState({ error: null });

render() {
const {
LoadingComponent,
vm,
virtualMachines,
namespaces,
persistentVolumeClaims,
dataVolumes,
requestsDatavolumes,
requestsPVCs,
loadError,
} = this.props;

const formFields = getFormFields(
this.props.namespaces,
this.props.vm,
this.props.persistentVolumeClaims,
this.props.dataVolumes,
this.props.virtualMachines
getLoadedData(namespaces, []),
vm,
getLoadedData(persistentVolumeClaims, []),
getLoadedData(dataVolumes, []),
getLoadedData(virtualMachines, [])
);
const { LoadingComponent } = this.props;

const dataVolumesValid = requestsDatavolumes ? dataVolumes && dataVolumes.loaded && !dataVolumes.loadError : true;
const pvcsValid = requestsPVCs
? persistentVolumeClaims && persistentVolumeClaims.loaded && !persistentVolumeClaims.loadError
: true;

const footer = this.state.cloning ? (
<LoadingComponent />
) : (
<React.Fragment>
<Button bsStyle="default" className="btn-cancel" onClick={this.props.onClose}>
<Button bsStyle="default" className="btn-cancel" onClick={this.props.cancel}>
Cancel
</Button>
<Button bsStyle="primary" onClick={this.cloneVm} disabled={!this.state.valid}>
<Button
bsStyle="primary"
onClick={this.cloneVm}
disabled={!(this.state.valid && dataVolumesValid && pvcsValid)}
>
Clone Virtual Machine
</Button>
</React.Fragment>
);
return (
<Modal show dialogClassName="kubevirt-clone-dialog">
<Modal.Header>
<Button className="close" onClick={this.props.onClose}>
<Button className="close" onClick={this.props.close}>
<Icon type="pf" name="close" />
</Button>
<Modal.Title>Clone Virtual Machine</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="kubevirt-clone-dialog__content">
{loadError && <Alert type="error">{loadError.message}</Alert>}
{isVmRunning(this.props.vm) && (
<Alert type="warning">
The VM {getName(this.props.vm)} is still running. It will be powered off while cloning.
Expand All @@ -182,16 +213,83 @@ export class CloneDialog extends React.Component {

CloneDialog.propTypes = {
vm: PropTypes.object.isRequired,
namespaces: PropTypes.array.isRequired,
persistentVolumeClaims: PropTypes.array.isRequired,
LoadingComponent: PropTypes.func,
virtualMachines: PropTypes.array.isRequired,
k8sCreate: PropTypes.func.isRequired,
k8sPatch: PropTypes.func.isRequired,
dataVolumes: PropTypes.array.isRequired,
onClose: PropTypes.func.isRequired,
namespaces: PropTypes.object,
persistentVolumeClaims: PropTypes.object,
virtualMachines: PropTypes.object,
dataVolumes: PropTypes.object,
requestsDatavolumes: PropTypes.bool,
requestsPVCs: PropTypes.bool,
loadError: PropTypes.object,
LoadingComponent: PropTypes.func,
close: PropTypes.func.isRequired,
cancel: PropTypes.func.isRequired,
};

CloneDialog.defaultProps = {
LoadingComponent: Loading,
namespaces: null,
persistentVolumeClaims: null,
virtualMachines: null,
dataVolumes: null,
requestsDatavolumes: false,
requestsPVCs: false,
loadError: null,
};

// eslint-disable-next-line react/no-multi-comp
export class CloneVMModalFirehose extends React.Component {
constructor(props) {
super(props);
this.state = {
namespace: getNamespace(props.vm),
};
}

render() {
const { vm, Firehose } = this.props;
const { namespace } = this.state;

const requestsDatavolumes = !!getVolumes(vm).find(v => v.dataVolume && v.dataVolume.name);
const requestsPVCs = !!getVolumes(vm).find(v => v.persistentVolumeClaim && v.persistentVolumeClaim.claimName);

const resources = [
getResource(NamespaceModel, { prop: 'namespaces' }),
getResource(VirtualMachineModel, { namespace, prop: 'virtualMachines' }),
];

if (requestsPVCs) {
resources.push(getResource(PersistentVolumeClaimModel, { namespace, prop: 'persistentVolumeClaims' }));
}

if (requestsDatavolumes) {
resources.push(getResource(DataVolumeModel, { namespace, prop: 'dataVolumes' }));
}

return (
<Firehose resources={resources}>
<CloneDialog
{...this.props}
onNamespaceChanged={n => this.setState({ namespace: n })}
requestsDatavolumes={requestsDatavolumes}
requestsPVCs={requestsPVCs}
/>
</Firehose>
);
}
}

CloneVMModalFirehose.propTypes = {
vm: PropTypes.object.isRequired,
Firehose: PropTypes.object.isRequired,
LoadingComponent: PropTypes.func,
k8sCreate: PropTypes.func.isRequired,
k8sPatch: PropTypes.func.isRequired,
close: PropTypes.func.isRequired,
cancel: PropTypes.func.isRequired,
};

CloneVMModalFirehose.defaultProps = {
LoadingComponent: Loading,
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ export default {
component: CloneDialog,
props: {
vm: cloudInitTestVm,
namespaces,
persistentVolumeClaims: [],
dataVolumes: [],
namespaces: { data: namespaces, loaded: true },
persistentVolumeClaims: { data: [], loaded: true },
dataVolumes: { data: [], loaded: true },
k8sCreate,
k8sPatch,
units,
onClose: noop,
virtualMachines: [],
virtualMachines: { data: [], loaded: true },
},
};
2 changes: 1 addition & 1 deletion src/components/Dialog/CloneDialog/index.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { CloneDialog } from './CloneDialog';
export { CloneDialog, CloneVMModalFirehose } from './CloneDialog';
8 changes: 7 additions & 1 deletion src/components/Dialog/CloneDialog/tests/CloneDialog.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ import { flushPromises, setCheckbox, setInput, clickButton, selectDropdownItem }
jest.mock('../../../../k8s/clone');

const testCloneDialog = (vms = [], onClose = noop, vm = cloudInitTestVm) => (
<CloneDialog {...CloneDialogFixture.props} virtualMachines={vms} onClose={onClose} vm={vm} k8sCreate={k8sCreate} />
<CloneDialog
{...CloneDialogFixture.props}
virtualMachines={{ data: vms, loaded: true }}
onClose={onClose}
vm={vm}
k8sCreate={k8sCreate}
/>
);

const setVmName = (component, value) => setInput(component.find('#vm-name').find(Text), value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ exports[`<CloneDialog /> renders correctly 1`] = `
bsStyle="default"
className="close"
disabled={false}
onClick={[Function]}
>
<Icon
name="close"
Expand Down Expand Up @@ -138,7 +137,6 @@ exports[`<CloneDialog /> renders correctly 1`] = `
bsStyle="default"
className="btn-cancel"
disabled={false}
onClick={[Function]}
>
Cancel
</Button>
Expand Down

0 comments on commit 195dfbc

Please sign in to comment.