Delete Pod UI (#1381)

Co-authored-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
adfost
2022-01-25 10:33:51 -08:00
committed by GitHub
parent 6404a1b984
commit 4a10a81374
6 changed files with 188 additions and 7 deletions

View File

@@ -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,

View File

@@ -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"],

View File

@@ -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

View File

@@ -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);

View File

@@ -73,6 +73,7 @@ const PodsSummary = ({
const closeDeleteModalAndRefresh = (reloadData: boolean) => {
setDeleteOpen(false);
setLoadingPods(true);
};
const confirmDeletePod = (pod: IPodListElement) => {

View File

@@ -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));