Delete Pod UI (#1381)
Co-authored-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
@@ -2050,9 +2050,13 @@ func getTenantPodsResponse(session *models.Principal, params operator_api.GetTen
|
||||
if len(pod.Status.ContainerStatuses) > 0 {
|
||||
restarts = int64(pod.Status.ContainerStatuses[0].RestartCount)
|
||||
}
|
||||
status := string(pod.Status.Phase)
|
||||
if pod.DeletionTimestamp != nil {
|
||||
status = "Terminating"
|
||||
}
|
||||
retval = append(retval, &models.TenantPod{
|
||||
Name: swag.String(pod.Name),
|
||||
Status: string(pod.Status.Phase),
|
||||
Status: status,
|
||||
TimeCreated: pod.CreationTimestamp.Unix(),
|
||||
PodIP: pod.Status.PodIP,
|
||||
Restarts: restarts,
|
||||
|
||||
@@ -95,12 +95,16 @@ func getPVCsResponse(session *models.Principal) (*models.ListPVCsResponse, *mode
|
||||
var ListPVCs []*models.PvcsListResponse
|
||||
|
||||
for _, pvc := range listAllPvcs.Items {
|
||||
status := string(pvc.Status.Phase)
|
||||
if pvc.DeletionTimestamp != nil {
|
||||
status = "Terminating"
|
||||
}
|
||||
pvcResponse := models.PvcsListResponse{
|
||||
Name: pvc.Name,
|
||||
Age: pvc.CreationTimestamp.String(),
|
||||
Capacity: pvc.Status.Capacity.Storage().String(),
|
||||
Namespace: pvc.Namespace,
|
||||
Status: string(pvc.Status.Phase),
|
||||
Status: status,
|
||||
StorageClass: *pvc.Spec.StorageClassName,
|
||||
Volume: pvc.Spec.VolumeName,
|
||||
Tenant: pvc.Labels["v1.min.io/tenant"],
|
||||
@@ -138,12 +142,16 @@ func getPVCsForTenantResponse(session *models.Principal, params operator_api.Lis
|
||||
var ListPVCs []*models.PvcsListResponse
|
||||
|
||||
for _, pvc := range listAllPvcs.Items {
|
||||
status := string(pvc.Status.Phase)
|
||||
if pvc.DeletionTimestamp != nil {
|
||||
status = "Terminating"
|
||||
}
|
||||
pvcResponse := models.PvcsListResponse{
|
||||
Name: pvc.Name,
|
||||
Age: pvc.CreationTimestamp.String(),
|
||||
Capacity: pvc.Status.Capacity.Storage().String(),
|
||||
Namespace: pvc.Namespace,
|
||||
Status: string(pvc.Status.Phase),
|
||||
Status: status,
|
||||
StorageClass: *pvc.Spec.StorageClassName,
|
||||
Volume: pvc.Spec.VolumeName,
|
||||
Tenant: pvc.Labels["v1.min.io/tenant"],
|
||||
|
||||
@@ -33,6 +33,7 @@ import { ErrorResponseHandler } from "../../../common/types";
|
||||
import api from "../../../common/api";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import SearchIcon from "../../../icons/SearchIcon";
|
||||
import DeletePVC from "../Tenants/TenantDetails/DeletePVC";
|
||||
|
||||
interface IStorageVolumesProps {
|
||||
classes: any;
|
||||
@@ -56,6 +57,8 @@ const StorageVolumes = ({
|
||||
const [records, setRecords] = useState<IStoragePVCs[]>([]);
|
||||
const [filter, setFilter] = useState("");
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [selectedPVC, setSelectedPVC] = useState<any>(null);
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
@@ -77,6 +80,16 @@ const StorageVolumes = ({
|
||||
elementItem.name.includes(filter)
|
||||
);
|
||||
|
||||
const confirmDeletePVC = (pvcItem: IStoragePVCs) => {
|
||||
const delPvc = {
|
||||
...pvcItem,
|
||||
tenant: pvcItem.tenant,
|
||||
namespace: pvcItem.namespace,
|
||||
};
|
||||
setSelectedPVC(delPvc);
|
||||
setDeleteOpen(true);
|
||||
};
|
||||
|
||||
const tableActions = [
|
||||
{
|
||||
type: "view",
|
||||
@@ -86,10 +99,23 @@ const StorageVolumes = ({
|
||||
);
|
||||
},
|
||||
},
|
||||
{ type: "delete", onClick: confirmDeletePVC },
|
||||
];
|
||||
|
||||
const closeDeleteModalAndRefresh = (reloadData: boolean) => {
|
||||
setDeleteOpen(false);
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{deleteOpen && (
|
||||
<DeletePVC
|
||||
deleteOpen={deleteOpen}
|
||||
selectedPVC={selectedPVC}
|
||||
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
||||
/>
|
||||
)}
|
||||
<h1 className={classes.sectionTitle}>Persistent Volumes Claims</h1>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { DialogContentText } from "@mui/material";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { connect } from "react-redux";
|
||||
import { setErrorSnackMessage } from "../../../../actions";
|
||||
import { ErrorResponseHandler } from "../../../../common/types";
|
||||
import useApi from "../../Common/Hooks/useApi";
|
||||
import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog";
|
||||
import { ConfirmDeleteIcon } from "../../../../icons";
|
||||
import { IStoragePVCs } from "../../Storage/types";
|
||||
|
||||
interface IDeletePVC {
|
||||
deleteOpen: boolean;
|
||||
selectedPVC: IStoragePVCs;
|
||||
closeDeleteModalAndRefresh: (refreshList: boolean) => any;
|
||||
setErrorSnackMessage: typeof setErrorSnackMessage;
|
||||
}
|
||||
|
||||
const DeletePVC = ({
|
||||
deleteOpen,
|
||||
selectedPVC,
|
||||
closeDeleteModalAndRefresh,
|
||||
setErrorSnackMessage,
|
||||
}: IDeletePVC) => {
|
||||
const [retypePVC, setRetypePVC] = useState("");
|
||||
|
||||
const onDelSuccess = () => closeDeleteModalAndRefresh(true);
|
||||
const onDelError = (err: ErrorResponseHandler) => setErrorSnackMessage(err);
|
||||
const onClose = () => closeDeleteModalAndRefresh(false);
|
||||
|
||||
const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError);
|
||||
|
||||
const onConfirmDelete = () => {
|
||||
if (retypePVC !== selectedPVC.name) {
|
||||
setErrorSnackMessage({
|
||||
errorMessage: "PVC name is incorrect",
|
||||
detailedError: "",
|
||||
});
|
||||
return;
|
||||
}
|
||||
invokeDeleteApi(
|
||||
"DELETE",
|
||||
`/api/v1/namespaces/${selectedPVC.namespace}/tenants/${selectedPVC.tenant}/pvc/${selectedPVC.name}`
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfirmDialog
|
||||
title={`Delete PVC`}
|
||||
confirmText={"Delete"}
|
||||
isOpen={deleteOpen}
|
||||
titleIcon={<ConfirmDeleteIcon />}
|
||||
isLoading={deleteLoading}
|
||||
onConfirm={onConfirmDelete}
|
||||
onClose={onClose}
|
||||
confirmButtonProps={{
|
||||
disabled: retypePVC !== selectedPVC.name || deleteLoading,
|
||||
}}
|
||||
confirmationContent={
|
||||
<DialogContentText>
|
||||
To continue please type <b>{selectedPVC.name}</b> in the box.
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="retype-PVC"
|
||||
name="retype-PVC"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setRetypePVC(event.target.value);
|
||||
}}
|
||||
label=""
|
||||
value={retypePVC}
|
||||
/>
|
||||
</Grid>
|
||||
</DialogContentText>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const connector = connect(null, {
|
||||
setErrorSnackMessage,
|
||||
});
|
||||
|
||||
export default connector(DeletePVC);
|
||||
@@ -73,6 +73,7 @@ const PodsSummary = ({
|
||||
|
||||
const closeDeleteModalAndRefresh = (reloadData: boolean) => {
|
||||
setDeleteOpen(false);
|
||||
setLoadingPods(true);
|
||||
};
|
||||
|
||||
const confirmDeletePod = (pod: IPodListElement) => {
|
||||
|
||||
@@ -27,17 +27,24 @@ import {
|
||||
searchField,
|
||||
tableStyles,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { IPVCsResponse, IStoragePVCs } from "../../Storage/types";
|
||||
import { IStoragePVCs } from "../../Storage/types";
|
||||
import { setErrorSnackMessage } from "../../../../actions";
|
||||
import { ErrorResponseHandler } from "../../../../common/types";
|
||||
import api from "../../../../common/api";
|
||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import SearchIcon from "../../../../icons/SearchIcon";
|
||||
import withSuspense from "../../Common/Components/withSuspense";
|
||||
import { setTenantDetailsLoad } from "../actions";
|
||||
import { AppState } from "../../../../store";
|
||||
|
||||
const DeletePVC = withSuspense(React.lazy(() => import("./DeletePVC")));
|
||||
|
||||
interface ITenantVolumesProps {
|
||||
classes: any;
|
||||
setErrorSnackMessage: typeof setErrorSnackMessage;
|
||||
match: any;
|
||||
loadingTenant: boolean;
|
||||
setTenantDetailsLoad: typeof setTenantDetailsLoad;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -55,10 +62,13 @@ const TenantVolumes = ({
|
||||
classes,
|
||||
setErrorSnackMessage,
|
||||
match,
|
||||
loadingTenant,
|
||||
}: ITenantVolumesProps) => {
|
||||
const [records, setRecords] = useState<IStoragePVCs[]>([]);
|
||||
const [filter, setFilter] = useState("");
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [selectedPVC, setSelectedPVC] = useState<any>(null);
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
|
||||
const tenantName = match.params["tenantName"];
|
||||
const tenantNamespace = match.params["tenantNamespace"];
|
||||
@@ -70,7 +80,7 @@ const TenantVolumes = ({
|
||||
"GET",
|
||||
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/pvcs`
|
||||
)
|
||||
.then((res: IPVCsResponse) => {
|
||||
.then((res: IStoragePVCs) => {
|
||||
let volumes = get(res, "pvcs", []);
|
||||
setRecords(volumes ? volumes : []);
|
||||
setLoading(false);
|
||||
@@ -82,12 +92,40 @@ const TenantVolumes = ({
|
||||
}
|
||||
}, [loading, setErrorSnackMessage, tenantName, tenantNamespace]);
|
||||
|
||||
const confirmDeletePVC = (pvcItem: IStoragePVCs) => {
|
||||
const delPvc = {
|
||||
...pvcItem,
|
||||
tenant: tenantName,
|
||||
namespace: tenantNamespace,
|
||||
};
|
||||
setSelectedPVC(delPvc);
|
||||
setDeleteOpen(true);
|
||||
};
|
||||
|
||||
const filteredRecords: IStoragePVCs[] = records.filter((elementItem) =>
|
||||
elementItem.name.includes(filter)
|
||||
);
|
||||
|
||||
const closeDeleteModalAndRefresh = (reloadData: boolean) => {
|
||||
setDeleteOpen(false);
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingTenant) {
|
||||
setLoading(true);
|
||||
}
|
||||
}, [loadingTenant]);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{deleteOpen && (
|
||||
<DeletePVC
|
||||
deleteOpen={deleteOpen}
|
||||
selectedPVC={selectedPVC}
|
||||
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
||||
/>
|
||||
)}
|
||||
<h1 className={classes.sectionTitle}>Volumes</h1>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
@@ -114,7 +152,7 @@ const TenantVolumes = ({
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.tableBlock}>
|
||||
<TableWrapper
|
||||
itemActions={[]}
|
||||
itemActions={[{ type: "delete", onClick: confirmDeletePVC }]}
|
||||
columns={[
|
||||
{
|
||||
label: "Name",
|
||||
@@ -146,10 +184,14 @@ const TenantVolumes = ({
|
||||
);
|
||||
};
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
loadingTenant: state.tenants.tenantDetails.loadingTenant,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
setErrorSnackMessage,
|
||||
};
|
||||
|
||||
const connector = connect(null, mapDispatchToProps);
|
||||
const connector = connect(mapState, mapDispatchToProps);
|
||||
|
||||
export default withStyles(styles)(connector(TenantVolumes));
|
||||
|
||||
Reference in New Issue
Block a user