Skip to content

Commit 7bf9887

Browse files
author
Adam Stafford
committed
Delete pods
1 parent a0e4a62 commit 7bf9887

File tree

12 files changed

+775
-3
lines changed

12 files changed

+775
-3
lines changed

portal-ui/src/screens/Console/Tenants/ListTenants/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ export interface IPodListElement {
4545
restarts: number;
4646
node: string;
4747
time: string;
48+
namespace?: string;
49+
tenant?: string;
4850
}
4951

5052
export interface IAddPoolRequest {
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2021 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
import React, { useEffect, useState } from "react";
18+
import {
19+
Button,
20+
Dialog,
21+
DialogActions,
22+
DialogContent,
23+
DialogContentText,
24+
DialogTitle,
25+
LinearProgress,
26+
} from "@material-ui/core";
27+
import api from "../../../../common/api";
28+
import { IPodListElement, ITenant } from "../ListTenants/types";
29+
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
30+
import Grid from "@material-ui/core/Grid";
31+
import { connect } from "react-redux";
32+
import { setErrorSnackMessage } from "../../../../actions";
33+
34+
interface IDeletePod {
35+
deleteOpen: boolean;
36+
selectedPod: IPodListElement;
37+
closeDeleteModalAndRefresh: (refreshList: boolean) => any;
38+
setErrorSnackMessage: typeof setErrorSnackMessage;
39+
}
40+
41+
const DeletePod = ({
42+
deleteOpen,
43+
selectedPod,
44+
closeDeleteModalAndRefresh,
45+
setErrorSnackMessage,
46+
}: IDeletePod) => {
47+
const [deleteLoading, setDeleteLoading] = useState(false);
48+
const [retypePod, setRetypePod] = useState("");
49+
50+
useEffect(() => {
51+
if (deleteLoading) {
52+
api
53+
.invoke(
54+
"DELETE",
55+
`/api/v1/namespaces/${selectedPod.namespace}/tenants/${selectedPod.tenant}/pods/${selectedPod.name}`
56+
)
57+
.then(() => {
58+
setDeleteLoading(false);
59+
closeDeleteModalAndRefresh(true);
60+
})
61+
.catch((err) => {
62+
setDeleteLoading(false);
63+
setErrorSnackMessage(err);
64+
});
65+
}
66+
// eslint-disable-next-line react-hooks/exhaustive-deps
67+
}, [deleteLoading]);
68+
69+
const removeRecord = () => {
70+
if (retypePod !== selectedPod.name) {
71+
setErrorSnackMessage("Tenant name is not correct");
72+
return;
73+
}
74+
setDeleteLoading(true);
75+
};
76+
77+
return (
78+
<Dialog
79+
open={deleteOpen}
80+
onClose={() => {
81+
closeDeleteModalAndRefresh(false);
82+
}}
83+
aria-labelledby="alert-dialog-title"
84+
aria-describedby="alert-dialog-description"
85+
>
86+
<DialogTitle id="alert-dialog-title">Delete Pod</DialogTitle>
87+
<DialogContent>
88+
{deleteLoading && <LinearProgress />}
89+
<DialogContentText id="alert-dialog-description">
90+
To continue please type <b>{selectedPod.name}</b> in the box.
91+
<Grid item xs={12}>
92+
<InputBoxWrapper
93+
id="retype-pod"
94+
name="retype-pod"
95+
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
96+
setRetypePod(event.target.value);
97+
}}
98+
label=""
99+
value={retypePod}
100+
/>
101+
</Grid>
102+
</DialogContentText>
103+
</DialogContent>
104+
<DialogActions>
105+
<Button
106+
onClick={() => {
107+
closeDeleteModalAndRefresh(false);
108+
}}
109+
color="primary"
110+
disabled={deleteLoading}
111+
>
112+
Cancel
113+
</Button>
114+
<Button
115+
onClick={removeRecord}
116+
color="secondary"
117+
autoFocus
118+
disabled={retypePod !== selectedPod.name}
119+
>
120+
Delete
121+
</Button>
122+
</DialogActions>
123+
</Dialog>
124+
);
125+
};
126+
127+
const connector = connect(null, {
128+
setErrorSnackMessage,
129+
});
130+
131+
export default connector(DeletePod);

portal-ui/src/screens/Console/Tenants/TenantDetails/PodsSummary.tsx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import api from "../../../../common/api";
2828
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
2929
import { AppState } from "../../../../store";
3030
import { setTenantDetailsLoad } from "../actions";
31+
import DeletePod from "./DeletePod";
3132

3233
interface IPodsSummary {
3334
match: any;
@@ -45,6 +46,9 @@ const styles = (theme: Theme) =>
4546
const PodsSummary = ({ match, history, loadingTenant }: IPodsSummary) => {
4647
const [pods, setPods] = useState<IPodListElement[]>([]);
4748
const [loadingPods, setLoadingPods] = useState<boolean>(true);
49+
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
50+
const [selectedPod, setSelectedPod] = useState<any>(null);
51+
const [isLoading, setIsLoading] = useState<boolean>(false);
4852

4953
const tenantName = match.params["tenantName"];
5054
const tenantNamespace = match.params["tenantNamespace"];
@@ -55,7 +59,26 @@ const PodsSummary = ({ match, history, loadingTenant }: IPodsSummary) => {
5559
);
5660
return;
5761
};
58-
const podTableActions = [{ type: "view", onClick: podViewAction }];
62+
63+
const closeDeleteModalAndRefresh = (reloadData: boolean) => {
64+
setDeleteOpen(false);
65+
66+
if (reloadData) {
67+
setIsLoading(true);
68+
}
69+
};
70+
71+
const confirmDeletePod = (pod: IPodListElement) => {
72+
pod.tenant = tenantName;
73+
pod.namespace = tenantNamespace;
74+
setSelectedPod(pod);
75+
setDeleteOpen(true);
76+
};
77+
78+
const podTableActions = [
79+
{ type: "view", onClick: podViewAction },
80+
{ type: "delete", onClick: confirmDeletePod },
81+
];
5982

6083
useEffect(() => {
6184
if (loadingTenant) {
@@ -88,6 +111,13 @@ const PodsSummary = ({ match, history, loadingTenant }: IPodsSummary) => {
88111

89112
return (
90113
<Fragment>
114+
{deleteOpen && (
115+
<DeletePod
116+
deleteOpen={deleteOpen}
117+
selectedPod={selectedPod}
118+
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
119+
/>
120+
)}
91121
<br />
92122
<TableWrapper
93123
columns={[

portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
setTenantInfo,
3131
setTenantTab,
3232
} from "../actions";
33-
import { ITenant } from "../ListTenants/types";
33+
import { IPodListElement, ITenant } from "../ListTenants/types";
3434
import {
3535
containerForHeader,
3636
tenantDetailsStyles,
@@ -99,7 +99,6 @@ const TenantDetails = ({
9999

100100
const tenantName = match.params["tenantName"];
101101
const tenantNamespace = match.params["tenantNamespace"];
102-
103102
const [anchorEl, setAnchorEl] = React.useState(null);
104103

105104
useEffect(() => {

restapi/admin_tenants.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,16 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
135135

136136
})
137137

138+
// Delete Pod
139+
api.AdminAPIDeletePodHandler = admin_api.DeletePodHandlerFunc(func(params admin_api.DeletePodParams, session *models.Principal) middleware.Responder {
140+
err := getDeletePodResponse(session, params)
141+
if err != nil {
142+
return admin_api.NewTenantInfoDefault(int(err.Code)).WithPayload(err)
143+
}
144+
return admin_api.NewTenantInfoOK()
145+
146+
})
147+
138148
// Update Tenant
139149
api.AdminAPIUpdateTenantHandler = admin_api.UpdateTenantHandlerFunc(func(params admin_api.UpdateTenantParams, session *models.Principal) middleware.Responder {
140150
err := getUpdateTenantResponse(session, params)
@@ -288,6 +298,24 @@ func deleteTenantAction(
288298
return nil
289299
}
290300

301+
// getDeleteTenantResponse gets the output of deleting a minio instance
302+
func getDeletePodResponse(session *models.Principal, params admin_api.DeletePodParams) *models.Error {
303+
ctx := context.Background()
304+
// get Kubernetes Client
305+
clientset, err := cluster.K8sClient(session.STSSessionToken)
306+
if err != nil {
307+
return prepareError(err)
308+
}
309+
listOpts := metav1.ListOptions{
310+
LabelSelector: fmt.Sprintf("v1.min.io/tenant=%s", params.Tenant),
311+
FieldSelector: fmt.Sprintf("metadata.name=%s%s", params.Tenant, params.PodName[len(params.Tenant):]),
312+
}
313+
if err = clientset.CoreV1().Pods(params.Namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOpts); err != nil {
314+
return prepareError(err)
315+
}
316+
return nil
317+
}
318+
291319
// GetTenantServiceURL gets tenant's service url with the proper scheme and port
292320
func GetTenantServiceURL(mi *miniov2.Tenant) (svcURL string) {
293321
scheme := "http"

restapi/embedded_spec.go

Lines changed: 76 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)