New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add option to create snapshot for PVC #5893
Add option to create snapshot for PVC #5893
Conversation
|
Folder name inside the console app should be volume-snapshot & it should hold all the related components. I see some inconsistency in your & bipul's PR's related to naming. |
} | ||
|
||
.co-volume-snapshot__form { | ||
margin: 20px 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should use PF variables, also for co-volume-snapshot__details-body
namespace: string; | ||
pvc: string; | ||
status: string; | ||
persistentVolumeClaim: K8sResourceKind; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would be nice to create a PersistentVolumeClaimKind
export const PVCDropdown: React.FC<PVCDropdownProps> = (props) => { | ||
const kind = 'PersistentVolumeClaim'; | ||
const { namespace, selectedKey } = props; | ||
const resources = [{ kind, namespace }]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const resources = [{ kind, namespace }]; | |
const resources = [{ PersistentVolumeClaimModel.kind, namespace }]; |
); | ||
}; | ||
|
||
// eslint-disable-next-line no-underscore-dangle |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you should not disable the rule
the VolumeSnapshot_ can be written without dongle (and the current one is also missing types)
// eslint-disable-next-line no-underscore-dangle | |
export const VolumeSnapshot = connectToPlural<VolumeSnapshotProps>( | |
({ kindObj, kindsInFlight, match: { params } }) => { | |
if (!kindObj && kindsInFlight) { | |
return <LoadingBox />; | |
} | |
return ( | |
<CreateSnapshotForm namespace={params.ns} resourceName={params.name} kindObj={kindObj} /> | |
); | |
}, | |
); | |
type VolumeSnapshotProps = WithPluralProps & { | |
match: match<{ name: string; ns: string }>; | |
}; |
const { selectedKey, pvcProvisioner } = props; | ||
const resources = [{ kind }]; | ||
const provisionerFilter = (snapshotClass: VolumeSnapshotClassKind) => { | ||
return pvcProvisioner && pvcProvisioner.includes(snapshotClass?.driver); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: it can be returned directly. No need to use return.
const [snapshotClassName, setSnapshotClassName] = React.useState<string>(); | ||
const [error, setError] = React.useState(''); | ||
const [inProgress, setInProgress] = React.useState(false); | ||
const [pvcStatus, setPVCStatus] = React.useState<string>(null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be initialized with empty string instead of null here? I don't see that the value null
is being used anywhere. Same for pvcProvisioner
event.preventDefault(); | ||
setInProgress(true); | ||
const snapshotTemplate: K8sResourceKind = { | ||
apiVersion: `${VolumeSnapshotModel.apiGroup}/${VolumeSnapshotModel.apiVersion}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
apiVersion: `${VolumeSnapshotModel.apiGroup}/${VolumeSnapshotModel.apiVersion}`, | |
apiVersion: `apiVersionForModel(VolumeSnapshotModel) |
} | ||
}; | ||
|
||
setValue(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setValue(); | |
updateValues(); |
}) | ||
.catch((err) => { | ||
setError(err.message || 'Could not create volume snapshot'); | ||
setInProgress(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can create a finally block for setInProgress(false)
.finally(() => setInProgress(false);
}); | ||
}; | ||
|
||
const title = 'Create VolumeSnapshot'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: move this variable definition after states declaration
Creating snapshot for claim <strong>{resourceName}</strong> | ||
</p> | ||
)} | ||
{pvcStatus && pvcStatus !== 'Bound' && ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can directly use pvcStatus !== 'Bound'
here once you define the pvsStatus
with empty string
/hold |
frontend/public/module/k8s/types.ts
Outdated
|
||
export type VolumeSnapshotClassKind = K8sResourceCommon & { | ||
driver: string; | ||
deletePolicy: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
deletePolicy: string; | |
deletionPolicy: string; |
@media (max-width: 769px) { | ||
display: none; | ||
} | ||
|
||
@media (max-width: 992px) { | ||
display: none; | ||
} | ||
|
||
@media (min-width: 1200px) { | ||
max-width: 50%; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume .pf-u-display-none
and its breakpoint based variants should do the trick?
} from '@console/internal/models'; | ||
import { PVCDropdown } from '@console/internal/components/utils/pvc-dropdown'; | ||
|
||
import './_create-volume-snapshot.scss'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move it to the end of the import declarations.
persistentVolumeClaim, | ||
}) => { | ||
const storageClass = persistentVolumeClaim?.spec?.storageClassName || '-'; | ||
const requestedCapacity = persistentVolumeClaim?.status?.capacity?.storage || '-'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think we get these values as G/T/Mi
let's convert to G/T/MiB
before showing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We get the value as a string (eg. 1Gi
), so we need not convert.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that but when we are displaying it to the user lets show it with B
since that's the console standard.
status, | ||
persistentVolumeClaim, | ||
}) => { | ||
const storageClass = persistentVolumeClaim?.spec?.storageClassName || '-'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const storageClass = persistentVolumeClaim?.spec?.storageClassName || '-'; | |
const storageClass = persistentVolumeClaim?.spec?.storageClassName; |
Isn't SC a required field for PVC creation?
status={pvcStatus} | ||
pvc={pvcName} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
status={pvcStatus} | |
pvc={pvcName} |
setPVCStatus(pvc?.metadata?.deletionTimestamp ? 'Terminating' : pvc?.status?.phase); | ||
setPVCProvisioner(sc?.provisioner); | ||
} catch (err) { | ||
setError(err); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really don't think we should be showing the error if we cannot fetch the PVC list.
const setValue = async () => { | ||
try { | ||
const pvc = await fetchK8s<K8sResourceKind>(PersistentVolumeClaimModel, pvcName, namespace); | ||
const sc = await fetchK8s<StorageClassResourceKind>( | ||
StorageClassModel, | ||
pvc?.spec?.storageClassName, | ||
); | ||
setPVCObj(pvc); | ||
setPVCStatus(pvc?.metadata?.deletionTimestamp ? 'Terminating' : pvc?.status?.phase); | ||
setPVCProvisioner(sc?.provisioner); | ||
} catch (err) { | ||
setError(err); | ||
} | ||
}; | ||
|
||
setValue(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const setValue = async () => { | |
try { | |
const pvc = await fetchK8s<K8sResourceKind>(PersistentVolumeClaimModel, pvcName, namespace); | |
const sc = await fetchK8s<StorageClassResourceKind>( | |
StorageClassModel, | |
pvc?.spec?.storageClassName, | |
); | |
setPVCObj(pvc); | |
setPVCStatus(pvc?.metadata?.deletionTimestamp ? 'Terminating' : pvc?.status?.phase); | |
setPVCProvisioner(sc?.provisioner); | |
} catch (err) { | |
setError(err); | |
} | |
}; | |
setValue(); | |
( async () => { | |
try { | |
const pvc = await fetchK8s<K8sResourceKind>(PersistentVolumeClaimModel, pvcName, namespace); | |
const sc = await fetchK8s<StorageClassResourceKind>( | |
StorageClassModel, | |
pvc?.spec?.storageClassName, | |
); | |
setPVCObj(pvc); | |
setPVCStatus(pvc?.metadata?.deletionTimestamp ? 'Terminating' : pvc?.status?.phase); | |
setPVCProvisioner(sc?.provisioner); | |
} catch (err) { | |
setError(err); | |
} | |
})(); |
|
||
@media (max-width: 992px) { | ||
display: none; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't 992 cover 769 as well?
} | ||
if (resourceKind?.kind === VolumeSnapshotModel.kind) { | ||
return [RestorePVC, DeleteSnapshot]; | ||
return [ClonePVC]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cloudbehl I hope you have considered the creation of clone in your PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes & removal of stale objects is also taken care.
/hold cancel |
.then((sc) => setSCObj(sc)) | ||
.catch((error) => | ||
// eslint-disable-next-line no-console | ||
console.error(error), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should have a better error handling, console logging is not very user friendly
); | ||
}; | ||
|
||
const CreateSnapshotForm = withHandlePromise((props: SnapshotResourceProps) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const CreateSnapshotForm = withHandlePromise((props: SnapshotResourceProps) => { | |
const CreateSnapshotForm = withHandlePromise<SnapshotResourceProps>((props) => { |
}; | ||
}, [namespace]); | ||
|
||
const [data, loaded, loadError] = useK8sWatchResource(resourceWatch); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const [data, loaded, loadError] = useK8sWatchResource(resourceWatch); | |
const [data, loaded, loadError] = useK8sWatchResource<PersistentVolumeClaimKind[]>(resourceWatch); |
|
||
React.useEffect(() => { | ||
if (!_.isEmpty(data)) { | ||
const currentPVC = (data as PersistentVolumeClaimKind[]).find( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const currentPVC = (data as PersistentVolumeClaimKind[]).find( | |
const currentPVC = data.find( |
(pvc) => pvc.metadata.name === pvcName, | ||
); | ||
setPVCObj(currentPVC); | ||
loadError || setError(loadError); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be outside of if
. If error happens, data
can be empty.
/* eslint-disable jsx-a11y/label-has-associated-control */ | ||
<> | ||
<label className="control-label co-required" html-for="claimName"> | ||
Persistent Volume claim |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Persistent Volume claim | |
{PersistentVolumeClaimModel.label} |
|
||
const handlePVCName = (name: string) => { | ||
if (!_.isEmpty(data)) { | ||
const currentPVC = (data as PersistentVolumeClaimKind[]).find( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const currentPVC = (data as PersistentVolumeClaimKind[]).find( | |
const currentPVC = data.find( |
}) | ||
.catch((e) => { | ||
// eslint-disable-next-line no-console | ||
console.error(e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needs better handling
); | ||
}); | ||
|
||
const VolumeSnapshotComponent = (props: VolumeSnapshotComponentProps) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const VolumeSnapshotComponent = (props: VolumeSnapshotComponentProps) => { | |
const VolumeSnapshotComponent: React.FC<VolumeSnapshotComponentProps> = (props) => { |
|
||
React.useEffect(() => { | ||
k8sGet(StorageClassModel, pvcSC) | ||
.then((sc) => setSCObj(sc)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.then((sc) => setSCObj(sc)) | |
.then(setSCObj) |
const [pvcName, setPVCName] = React.useState(resourceName); | ||
const [pvcObj, setPVCObj] = React.useState(null); | ||
const [snapshotName, setSnapshotName] = React.useState(`${pvcName || 'pvc'}-snapshot`); | ||
const [snapshotClassName, setSnapshotClassName] = React.useState<string>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const [snapshotClassName, setSnapshotClassName] = React.useState<string>(); | |
const [snapshotClassName, setSnapshotClassName] = React.useState<string>(''); |
...nsole-app/src/components/volume-snapshot/create-volume-snapshot/_create-volume-snapshot.scss
Show resolved
Hide resolved
const [pvcObj, setPVCObj] = React.useState(null); | ||
const [snapshotName, setSnapshotName] = React.useState(`${pvcName || 'pvc'}-snapshot`); | ||
const [snapshotClassName, setSnapshotClassName] = React.useState<string>(); | ||
const [error, setError] = React.useState(null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const [error, setError] = React.useState(null); | |
const [error, setError] = React.useState(''); |
const resourceWatch = React.useMemo(() => { | ||
return { | ||
kind: PersistentVolumeClaimModel.kind, | ||
namespace, | ||
isList: true, | ||
}; | ||
}, [namespace]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const resourceWatch = React.useMemo(() => { | |
return { | |
kind: PersistentVolumeClaimModel.kind, | |
namespace, | |
isList: true, | |
}; | |
}, [namespace]); | |
const resourceWatch = React.useMemo(() => { | |
return Object.assign({ | |
kind: PersistentVolumeClaimModel.kind, | |
namespace, | |
isList: true, | |
}, pvcName ? { name: pvcName } : null); | |
}, [namespace, pvcName]); |
...console-app/src/components/volume-snapshot/create-volume-snapshot/create-volume-snapshot.tsx
Show resolved
Hide resolved
203b188
to
6f65827
Compare
...nsole-app/src/components/volume-snapshot/create-volume-snapshot/_create-volume-snapshot.scss
Show resolved
Hide resolved
const { resourceName, namespace, kindObj, handlePromise, inProgress, errorMessage } = props; | ||
|
||
const [pvcName, setPVCName] = React.useState(resourceName); | ||
const [pvcObj, setPVCObj] = React.useState(null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const [pvcObj, setPVCObj] = React.useState(null); | |
const [pvcObj, setPVCObj] = React.useState<PersistentVolumeClaimKind>(null); |
...console-app/src/components/volume-snapshot/create-volume-snapshot/create-volume-snapshot.tsx
Show resolved
Hide resolved
...console-app/src/components/volume-snapshot/create-volume-snapshot/create-volume-snapshot.tsx
Show resolved
Hide resolved
...console-app/src/components/volume-snapshot/create-volume-snapshot/create-volume-snapshot.tsx
Show resolved
Hide resolved
match: { | ||
params: { | ||
ns: string; | ||
plural: string; | ||
}; | ||
}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
match: { | |
params: { | |
ns: string; | |
plural: string; | |
}; | |
}; | |
}; | |
match: match<{ns?: string, plural?:string}>; |
frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx
Show resolved
Hide resolved
.catch((e) => { | ||
throw e; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why catch just to throw again?
@bipuladh as per offline conversations, have resolved some comments, and addressed other comments. Please review. |
/approve |
PersistentVolumeModel, | ||
} from '@console/internal/models'; | ||
import { PVCDropdown } from '@console/internal/components/utils/pvc-dropdown'; | ||
import { apiVersionForModel } from '@console/kubevirt-plugin/integration-tests/tests/utils/selectors'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dont import from kubevirt-plugin
}; | ||
|
||
const PVCSummary: React.FC<PVCSummaryProps> = ({ persistentVolumeClaim }) => { | ||
const accessModeLabels = Object.freeze({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you reuse https://github.com/openshift/console/blob/master/frontend/public/components/storage/shared.ts#L21
?
user to create in-time images of snapshot. Jira: https://issues.redhat.com/browse/RHSTOR-1154 Signed-off-by: Kanika <kmurarka@redhat.com>
/lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: a2batic, bipuladh, cloudbehl, rawagner The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
@@ -991,3 +991,19 @@ export type VolumeSnapshotClassKind = K8sResourceCommon & { | |||
deletionPolicy: string; | |||
driver: string; | |||
}; | |||
|
|||
export type PersistentVolumeClaimKind = K8sResourceCommon & { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: add source object to accommodate cloning
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack, as per offline converstaion, will be taking it up in follow-up PR.
|
||
const PVCSummary: React.FC<PVCSummaryProps> = ({ persistentVolumeClaim }) => { | ||
const storageClass = persistentVolumeClaim?.spec?.storageClassName; | ||
const requestedCapacity = persistentVolumeClaim?.spec?.resources?.requests?.storage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: This is required in the PVC object so we don't need to check all entities.
const requestedCapacity = persistentVolumeClaim?.spec?.resources?.requests?.storage; | |
const requestedCapacity = persistentVolumeClaim?.spec.resources.requests.storage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack, as per offline converstaion, will be taking it up in follow-up PR.
'Create Snapshot` action in kebab menu for PVC will allow
user to create in-time images of snapshot.
@bipuladh @cloudbehl
Signed-off-by: Kanika kmurarka@redhat.com