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 {
|
if len(pod.Status.ContainerStatuses) > 0 {
|
||||||
restarts = int64(pod.Status.ContainerStatuses[0].RestartCount)
|
restarts = int64(pod.Status.ContainerStatuses[0].RestartCount)
|
||||||
}
|
}
|
||||||
|
status := string(pod.Status.Phase)
|
||||||
|
if pod.DeletionTimestamp != nil {
|
||||||
|
status = "Terminating"
|
||||||
|
}
|
||||||
retval = append(retval, &models.TenantPod{
|
retval = append(retval, &models.TenantPod{
|
||||||
Name: swag.String(pod.Name),
|
Name: swag.String(pod.Name),
|
||||||
Status: string(pod.Status.Phase),
|
Status: status,
|
||||||
TimeCreated: pod.CreationTimestamp.Unix(),
|
TimeCreated: pod.CreationTimestamp.Unix(),
|
||||||
PodIP: pod.Status.PodIP,
|
PodIP: pod.Status.PodIP,
|
||||||
Restarts: restarts,
|
Restarts: restarts,
|
||||||
|
|||||||
@@ -95,12 +95,16 @@ func getPVCsResponse(session *models.Principal) (*models.ListPVCsResponse, *mode
|
|||||||
var ListPVCs []*models.PvcsListResponse
|
var ListPVCs []*models.PvcsListResponse
|
||||||
|
|
||||||
for _, pvc := range listAllPvcs.Items {
|
for _, pvc := range listAllPvcs.Items {
|
||||||
|
status := string(pvc.Status.Phase)
|
||||||
|
if pvc.DeletionTimestamp != nil {
|
||||||
|
status = "Terminating"
|
||||||
|
}
|
||||||
pvcResponse := models.PvcsListResponse{
|
pvcResponse := models.PvcsListResponse{
|
||||||
Name: pvc.Name,
|
Name: pvc.Name,
|
||||||
Age: pvc.CreationTimestamp.String(),
|
Age: pvc.CreationTimestamp.String(),
|
||||||
Capacity: pvc.Status.Capacity.Storage().String(),
|
Capacity: pvc.Status.Capacity.Storage().String(),
|
||||||
Namespace: pvc.Namespace,
|
Namespace: pvc.Namespace,
|
||||||
Status: string(pvc.Status.Phase),
|
Status: status,
|
||||||
StorageClass: *pvc.Spec.StorageClassName,
|
StorageClass: *pvc.Spec.StorageClassName,
|
||||||
Volume: pvc.Spec.VolumeName,
|
Volume: pvc.Spec.VolumeName,
|
||||||
Tenant: pvc.Labels["v1.min.io/tenant"],
|
Tenant: pvc.Labels["v1.min.io/tenant"],
|
||||||
@@ -138,12 +142,16 @@ func getPVCsForTenantResponse(session *models.Principal, params operator_api.Lis
|
|||||||
var ListPVCs []*models.PvcsListResponse
|
var ListPVCs []*models.PvcsListResponse
|
||||||
|
|
||||||
for _, pvc := range listAllPvcs.Items {
|
for _, pvc := range listAllPvcs.Items {
|
||||||
|
status := string(pvc.Status.Phase)
|
||||||
|
if pvc.DeletionTimestamp != nil {
|
||||||
|
status = "Terminating"
|
||||||
|
}
|
||||||
pvcResponse := models.PvcsListResponse{
|
pvcResponse := models.PvcsListResponse{
|
||||||
Name: pvc.Name,
|
Name: pvc.Name,
|
||||||
Age: pvc.CreationTimestamp.String(),
|
Age: pvc.CreationTimestamp.String(),
|
||||||
Capacity: pvc.Status.Capacity.Storage().String(),
|
Capacity: pvc.Status.Capacity.Storage().String(),
|
||||||
Namespace: pvc.Namespace,
|
Namespace: pvc.Namespace,
|
||||||
Status: string(pvc.Status.Phase),
|
Status: status,
|
||||||
StorageClass: *pvc.Spec.StorageClassName,
|
StorageClass: *pvc.Spec.StorageClassName,
|
||||||
Volume: pvc.Spec.VolumeName,
|
Volume: pvc.Spec.VolumeName,
|
||||||
Tenant: pvc.Labels["v1.min.io/tenant"],
|
Tenant: pvc.Labels["v1.min.io/tenant"],
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import { ErrorResponseHandler } from "../../../common/types";
|
|||||||
import api from "../../../common/api";
|
import api from "../../../common/api";
|
||||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||||
import SearchIcon from "../../../icons/SearchIcon";
|
import SearchIcon from "../../../icons/SearchIcon";
|
||||||
|
import DeletePVC from "../Tenants/TenantDetails/DeletePVC";
|
||||||
|
|
||||||
interface IStorageVolumesProps {
|
interface IStorageVolumesProps {
|
||||||
classes: any;
|
classes: any;
|
||||||
@@ -56,6 +57,8 @@ const StorageVolumes = ({
|
|||||||
const [records, setRecords] = useState<IStoragePVCs[]>([]);
|
const [records, setRecords] = useState<IStoragePVCs[]>([]);
|
||||||
const [filter, setFilter] = useState("");
|
const [filter, setFilter] = useState("");
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
const [selectedPVC, setSelectedPVC] = useState<any>(null);
|
||||||
|
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@@ -77,6 +80,16 @@ const StorageVolumes = ({
|
|||||||
elementItem.name.includes(filter)
|
elementItem.name.includes(filter)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const confirmDeletePVC = (pvcItem: IStoragePVCs) => {
|
||||||
|
const delPvc = {
|
||||||
|
...pvcItem,
|
||||||
|
tenant: pvcItem.tenant,
|
||||||
|
namespace: pvcItem.namespace,
|
||||||
|
};
|
||||||
|
setSelectedPVC(delPvc);
|
||||||
|
setDeleteOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
const tableActions = [
|
const tableActions = [
|
||||||
{
|
{
|
||||||
type: "view",
|
type: "view",
|
||||||
@@ -86,10 +99,23 @@ const StorageVolumes = ({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{ type: "delete", onClick: confirmDeletePVC },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const closeDeleteModalAndRefresh = (reloadData: boolean) => {
|
||||||
|
setDeleteOpen(false);
|
||||||
|
setLoading(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
{deleteOpen && (
|
||||||
|
<DeletePVC
|
||||||
|
deleteOpen={deleteOpen}
|
||||||
|
selectedPVC={selectedPVC}
|
||||||
|
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<h1 className={classes.sectionTitle}>Persistent Volumes Claims</h1>
|
<h1 className={classes.sectionTitle}>Persistent Volumes Claims</h1>
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
<TextField
|
<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) => {
|
const closeDeleteModalAndRefresh = (reloadData: boolean) => {
|
||||||
setDeleteOpen(false);
|
setDeleteOpen(false);
|
||||||
|
setLoadingPods(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmDeletePod = (pod: IPodListElement) => {
|
const confirmDeletePod = (pod: IPodListElement) => {
|
||||||
|
|||||||
@@ -27,17 +27,24 @@ import {
|
|||||||
searchField,
|
searchField,
|
||||||
tableStyles,
|
tableStyles,
|
||||||
} from "../../Common/FormComponents/common/styleLibrary";
|
} from "../../Common/FormComponents/common/styleLibrary";
|
||||||
import { IPVCsResponse, IStoragePVCs } from "../../Storage/types";
|
import { IStoragePVCs } from "../../Storage/types";
|
||||||
import { setErrorSnackMessage } from "../../../../actions";
|
import { setErrorSnackMessage } from "../../../../actions";
|
||||||
import { ErrorResponseHandler } from "../../../../common/types";
|
import { ErrorResponseHandler } from "../../../../common/types";
|
||||||
import api from "../../../../common/api";
|
import api from "../../../../common/api";
|
||||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||||
import SearchIcon from "../../../../icons/SearchIcon";
|
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 {
|
interface ITenantVolumesProps {
|
||||||
classes: any;
|
classes: any;
|
||||||
setErrorSnackMessage: typeof setErrorSnackMessage;
|
setErrorSnackMessage: typeof setErrorSnackMessage;
|
||||||
match: any;
|
match: any;
|
||||||
|
loadingTenant: boolean;
|
||||||
|
setTenantDetailsLoad: typeof setTenantDetailsLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
@@ -55,10 +62,13 @@ const TenantVolumes = ({
|
|||||||
classes,
|
classes,
|
||||||
setErrorSnackMessage,
|
setErrorSnackMessage,
|
||||||
match,
|
match,
|
||||||
|
loadingTenant,
|
||||||
}: ITenantVolumesProps) => {
|
}: ITenantVolumesProps) => {
|
||||||
const [records, setRecords] = useState<IStoragePVCs[]>([]);
|
const [records, setRecords] = useState<IStoragePVCs[]>([]);
|
||||||
const [filter, setFilter] = useState("");
|
const [filter, setFilter] = useState("");
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
const [selectedPVC, setSelectedPVC] = useState<any>(null);
|
||||||
|
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
const tenantName = match.params["tenantName"];
|
const tenantName = match.params["tenantName"];
|
||||||
const tenantNamespace = match.params["tenantNamespace"];
|
const tenantNamespace = match.params["tenantNamespace"];
|
||||||
@@ -70,7 +80,7 @@ const TenantVolumes = ({
|
|||||||
"GET",
|
"GET",
|
||||||
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/pvcs`
|
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/pvcs`
|
||||||
)
|
)
|
||||||
.then((res: IPVCsResponse) => {
|
.then((res: IStoragePVCs) => {
|
||||||
let volumes = get(res, "pvcs", []);
|
let volumes = get(res, "pvcs", []);
|
||||||
setRecords(volumes ? volumes : []);
|
setRecords(volumes ? volumes : []);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -82,12 +92,40 @@ const TenantVolumes = ({
|
|||||||
}
|
}
|
||||||
}, [loading, setErrorSnackMessage, tenantName, tenantNamespace]);
|
}, [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) =>
|
const filteredRecords: IStoragePVCs[] = records.filter((elementItem) =>
|
||||||
elementItem.name.includes(filter)
|
elementItem.name.includes(filter)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const closeDeleteModalAndRefresh = (reloadData: boolean) => {
|
||||||
|
setDeleteOpen(false);
|
||||||
|
setLoading(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (loadingTenant) {
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
|
}, [loadingTenant]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
{deleteOpen && (
|
||||||
|
<DeletePVC
|
||||||
|
deleteOpen={deleteOpen}
|
||||||
|
selectedPVC={selectedPVC}
|
||||||
|
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<h1 className={classes.sectionTitle}>Volumes</h1>
|
<h1 className={classes.sectionTitle}>Volumes</h1>
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
<TextField
|
<TextField
|
||||||
@@ -114,7 +152,7 @@ const TenantVolumes = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} className={classes.tableBlock}>
|
<Grid item xs={12} className={classes.tableBlock}>
|
||||||
<TableWrapper
|
<TableWrapper
|
||||||
itemActions={[]}
|
itemActions={[{ type: "delete", onClick: confirmDeletePVC }]}
|
||||||
columns={[
|
columns={[
|
||||||
{
|
{
|
||||||
label: "Name",
|
label: "Name",
|
||||||
@@ -146,10 +184,14 @@ const TenantVolumes = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mapState = (state: AppState) => ({
|
||||||
|
loadingTenant: state.tenants.tenantDetails.loadingTenant,
|
||||||
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
setErrorSnackMessage,
|
setErrorSnackMessage,
|
||||||
};
|
};
|
||||||
|
|
||||||
const connector = connect(null, mapDispatchToProps);
|
const connector = connect(mapState, mapDispatchToProps);
|
||||||
|
|
||||||
export default withStyles(styles)(connector(TenantVolumes));
|
export default withStyles(styles)(connector(TenantVolumes));
|
||||||
|
|||||||
Reference in New Issue
Block a user