Skip to content

Commit

Permalink
Add clone PVC feature
Browse files Browse the repository at this point in the history
Signed-off-by: Ankush Behl <cloudbehl@gmail.com>
  • Loading branch information
cloudbehl committed Jul 14, 2020
1 parent 7c4924b commit 4faf0bc
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 72 deletions.

This file was deleted.

20 changes: 0 additions & 20 deletions frontend/packages/ceph-storage-plugin/src/utils/clone-workflow.ts

This file was deleted.

@@ -1,4 +1,3 @@
import { ClonePVC } from './clone-workflow';
import { DeleteSnapshot } from './delete-snapshot-workflow';
import { K8sKind } from '@console/internal/module/k8s';
import { KebabAction } from '@console/internal/components/utils';
Expand All @@ -9,7 +8,7 @@ import { VolumeSnapshotModel } from '../models';

export const getKebabActionsForKind = (resourceKind: K8sKind): KebabAction[] => {
if (resourceKind?.kind === PersistentVolumeClaimModel.kind) {
return [SnapshotPVC, ClonePVC];
return [SnapshotPVC];
}
if (resourceKind?.kind === VolumeSnapshotModel.kind) {
return [RestorePVC, DeleteSnapshot];
Expand Down
@@ -0,0 +1,16 @@
.co-clone-pvc-modal__details {
margin-top: var(--pf-global--spacer--xl);
}

.clone-pvc-modal__details-section {
display: flex;
justify-content: space-between;
}

.clone-pvc-modal__pvc-details {
font-weight: var(--pf-global--FontWeight--bold);
}

.clone-pvc-modal__name--margin {
margin-bottom: var(--pf-global--spacer--sm);
}
Expand Up @@ -3,55 +3,69 @@ import './_clone-pvc-modal.scss';
import * as React from 'react';

import { Form, FormGroup, TextInput } from '@patternfly/react-core';
import { K8sResourceKind, k8sCreate } from '@console/internal/module/k8s';
import { K8sResourceKind, k8sCreate, referenceFor } from '@console/internal/module/k8s';
import {
LoadingInline,
ResourceIcon,
humanizeBinaryBytes,
history,
withHandlePromise,
} from '@console/internal/components/utils/index';
RequestSizeInput,
validate,
resourceObjPath,
} from '@console/internal/components/utils';
import {
ModalBody,
ModalComponentProps,
ModalSubmitFooter,
ModalTitle,
createModalLauncher,
} from '@console/internal/components/factory';

import { DataPoint } from '@console/internal/components/graphs/index';
import { DataPoint } from '@console/internal/components/graphs';
import { HandlePromiseProps } from '@console/internal/components/utils/promise-component';
import { PersistentVolumeClaimModel } from '@console/internal/models/index';
import { PersistentVolumeClaimModel } from '@console/internal/models';
import { PrometheusEndpoint } from '@console/internal/components/graphs/helpers';
import { getInstantVectorStats } from '@console/internal/components/graphs/utils';
import { getPVCUsedCapacityQuery } from '../../../constants/queries';
import { usePrometheusPoll } from '@console/internal/components/graphs/prometheus-poll-hook';
import { getRequestedPVCSize } from '@console/shared/src/selectors';

const accessModeLabels = Object.freeze({
ReadWriteOnce: 'Single User (RWO)',
ReadWriteMany: 'Shared Access (RWX)',
ReadOnlyMany: 'Read Only (ROX)',
});

const dropdownUnits = {
Mi: 'MiB',
Gi: 'GiB',
Ti: 'TiB',
};

const ClonePVCModal = withHandlePromise((props: ClonePVCModalProps) => {
const { close, cancel, resource, handlePromise, errorMessage, inProgress } = props;
const pvcName: string = resource.metadata.name;
const [clonePVCName, setClonePVCName] = React.useState(`${pvcName}-clone`);
const { name: pvcName, namespace } = resource?.metadata;
const defaultSize: string[] = validate.split(getRequestedPVCSize(resource));

const pvcUsedCapacityQuery: string = getPVCUsedCapacityQuery(pvcName);
const [clonePVCName, setClonePVCName] = React.useState(`${pvcName}-clone`);
const [requestedSize, setRequestedSize] = React.useState(defaultSize[0] || '');
const [requestedUnit, setRequestedUnit] = React.useState(defaultSize[1] || 'Gi');

const pvcUsedCapacityQuery: string = `kubelet_volume_stats_used_bytes{persistentvolumeclaim='${pvcName}'}`;
const [response, error, loading] = usePrometheusPoll({
endpoint: PrometheusEndpoint.QUERY,
query: pvcUsedCapacityQuery,
namespace: resource.metadata.namespace,
namespace,
});

const pvcUsedCapacityQueryResult: DataPoint[] = getInstantVectorStats(
response,
null,
humanizeBinaryBytes,
);

const pvcUsedCapacity = pvcUsedCapacityQueryResult?.[0]?.label || 'No Data';
const pvcUsedCapacity = pvcUsedCapacityQueryResult?.[0]?.label || '-';
const requestedSizeInputChange = ({ value, unit }) => {
setRequestedSize(value);
setRequestedUnit(unit);
};

const submit = (event: React.FormEvent<EventTarget>) => {
event.preventDefault();
Expand All @@ -70,74 +84,87 @@ const ClonePVCModal = withHandlePromise((props: ClonePVCModalProps) => {
kind: PersistentVolumeClaimModel.kind,
apiGroup: '',
},
resources: {
requests: {
storage: `${requestedSize}${requestedUnit}`,
},
},
accessModes: resource.spec.accessModes,
resources: resource.spec.resources,
},
};

return handlePromise(k8sCreate(PersistentVolumeClaimModel, pvcCloneObj)).then(close);
return handlePromise(k8sCreate(PersistentVolumeClaimModel, pvcCloneObj)).then(
(cloneResource) => {
close();
history.push(resourceObjPath(cloneResource, referenceFor(cloneResource)));
},
);
};

return (
<Form onSubmit={submit}>
<div className="modal-content modal-content--no-inner-scroll">
<ModalTitle>Clone</ModalTitle>
<ModalBody>
<FormGroup label="Name" isRequired fieldId="ceph-clone-pvc-modal__name">
<FormGroup label="Name" isRequired fieldId="clone-pvc-modal__name">
<TextInput
isRequired
type="text"
name="ceph-clone-pvc-modal__name"
className="clone-pvc-modal__name--margin"
value={clonePVCName}
onChange={setClonePVCName}
aria-label="Clone Pvc"
/>
</FormGroup>
<FormGroup label="Size" isRequired fieldId="clone-pvc-modal__size">
<RequestSizeInput
name="requestSize"
onChange={requestedSizeInputChange}
defaultRequestSizeUnit={requestedUnit}
defaultRequestSizeValue={requestedSize}
dropdownUnits={dropdownUnits}
required
/>
</FormGroup>
<div className="ceph-clone-pvc-modal__details">
<div className="co-clone-pvc-modal__details">
<p className="text-muted">PVC Details</p>
<div className="ceph-clone-pvc-modal__details-section">
<div className="clone-pvc-modal__details-section">
<div>
<div>
<p className="ceph-clone-pvc-modal__pvc-details">Namespace</p>
<p className="clone-pvc-modal__pvc-details">Namespace</p>
<p>
<ResourceIcon kind="Namespace" />
{resource.metadata.namespace}
</p>
</div>
<div>
<p className="ceph-clone-pvc-modal__pvc-details">Storage Class</p>
<p className="clone-pvc-modal__pvc-details">Storage Class</p>
<p>
{resource.spec.storageClassName ? (
<>
<ResourceIcon kind="StorageClass" />
{resource.spec.storageClassName}
</>
) : (
'None'
)}
<ResourceIcon kind="StorageClass" />
{resource.spec?.storageClassName || '-'}
</p>
</div>
</div>
<div>
<div>
<p className="ceph-clone-pvc-modal__pvc-details">Requested Capacity</p>
<p>{resource.spec.resources.requests.storage}</p>
<p className="clone-pvc-modal__pvc-details">Requested Capacity</p>
<p>{getRequestedPVCSize(resource)}</p>
</div>
<div>
<p className="ceph-clone-pvc-modal__pvc-details">Used Capacity</p>
<p className="clone-pvc-modal__pvc-details">Used Capacity</p>
<div>
{!loading && !error && pvcUsedCapacity}
{loading && <LoadingInline />}
{!loading && error && 'No Data'}
{!loading && error && '-'}
</div>
</div>
</div>
<div>
<div>
<p className="ceph-clone-pvc-modal__pvc-details">Access Mode</p>
<p className="clone-pvc-modal__pvc-details">Access Mode</p>
<p>{accessModeLabels[resource.spec.accessModes]}</p>
</div>
<div>
<p className="ceph-clone-pvc-modal__pvc-details">Volume Mode</p>
<p className="clone-pvc-modal__pvc-details">Volume Mode</p>
<p>{resource.spec.volumeMode}</p>
</div>
</div>
Expand Down
5 changes: 5 additions & 0 deletions frontend/public/components/modals/index.ts
Expand Up @@ -94,6 +94,11 @@ export const expandPVCModal = (props) =>
m.expandPVCModal(props),
);

export const clonePVCModal = (props) =>
import(
'@console/app/src/components/modals/clone/clone-pvc-modal' /* webpackChunkName: "clone-pvc-modal" */
).then((m) => m.default(props));

export const removeVolumeModal = (props) =>
import('./remove-volume-modal' /* webpackChunkName: "remove-volume-modal" */).then((m) =>
m.removeVolumeModal(props),
Expand Down
3 changes: 2 additions & 1 deletion frontend/public/components/persistent-volume-claim.jsx
Expand Up @@ -27,10 +27,11 @@ import { setPVCMetrics } from '../actions/ui';
import { PrometheusEndpoint } from './graphs/helpers';
import { usePrometheusPoll } from './graphs/prometheus-poll-hook';

const { common, ExpandPVC } = Kebab.factory;
const { common, ExpandPVC, ClonePVC } = Kebab.factory;
const menuActions = [
...Kebab.getExtensionsActionsForKind(PersistentVolumeClaimModel),
ExpandPVC,
ClonePVC,
...common,
];

Expand Down
10 changes: 10 additions & 0 deletions frontend/public/components/utils/kebab.tsx
Expand Up @@ -15,6 +15,7 @@ import {
podSelectorModal,
deleteModal,
expandPVCModal,
clonePVCModal,
} from '../modals';
import { asAccessReview, checkAccess, history, resourceObjPath, useAccessReview } from './index';
import {
Expand Down Expand Up @@ -329,6 +330,15 @@ const kebabFactory: KebabFactory = {
}),
accessReview: asAccessReview(kind, obj, 'patch'),
}),
ClonePVC: (kind, obj) => ({
label: 'Clone PVC',
callback: () =>
clonePVCModal({
kind,
resource: obj,
}),
accessReview: asAccessReview(kind, obj, 'create'),
}),
};

// The common menu actions that most resource share
Expand Down

0 comments on commit 4faf0bc

Please sign in to comment.