Skip to content

Commit 59d4961

Browse files
committed
app: home: Add delete button for clusters
Signed-off-by: Vincent T <[email protected]>
1 parent d24fc9f commit 59d4961

File tree

3 files changed

+75
-24
lines changed

3 files changed

+75
-24
lines changed

backend/cmd/headlamp.go

+14
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,20 @@ func (c *HeadlampConfig) deleteCluster(w http.ResponseWriter, r *http.Request) {
13841384
return
13851385
}
13861386

1387+
alsoRemoveFromKubeConfig := true
1388+
1389+
if alsoRemoveFromKubeConfig {
1390+
// delete context from actual deafult kubecofig file
1391+
// to do : replace the hard coding of this line with an actual path
1392+
err = kubeconfig.RemoveContextFromFile(name, filepath.Join("/home/vynty/.kube/config"))
1393+
if err != nil {
1394+
logger.Log(logger.LevelError, map[string]string{"cluster": name},
1395+
err, "my cool error")
1396+
http.Error(w, "removing cluster from kubeconfig", http.StatusInternalServerError)
1397+
return
1398+
}
1399+
}
1400+
13871401
kubeConfigPersistenceFile, err := defaultKubeConfigPersistenceFile()
13881402
if err != nil {
13891403
logger.Log(logger.LevelError, map[string]string{"cluster": name},

frontend/src/components/App/Home/index.tsx

+60-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Icon } from '@iconify/react';
2-
import { useTheme } from '@mui/material';
2+
import { Checkbox, useTheme } from '@mui/material';
33
import Box from '@mui/material/Box';
44
import IconButton from '@mui/material/IconButton';
55
import ListItemText from '@mui/material/ListItemText';
@@ -25,13 +25,32 @@ import { ConfirmDialog } from '../../common';
2525
import ResourceTable from '../../common/Resource/ResourceTable';
2626
import RecentClusters from './RecentClusters';
2727

28+
/**
29+
* Gets the origin of a cluster.
30+
*
31+
* @param cluster
32+
* @returns A description of where the cluster is picked up from: dynamic, in-cluster, or from a kubeconfig file.
33+
*/
34+
function getOrigin(cluster: Cluster): string {
35+
if (cluster.meta_data?.source === 'kubeconfig') {
36+
const kubeconfigPath = process.env.KUBECONFIG ?? '~/.kube/config';
37+
return `Kubeconfig: ${kubeconfigPath}`;
38+
} else if (cluster.meta_data?.source === 'dynamic_cluster') {
39+
return t('translation|Plugin');
40+
} else if (cluster.meta_data?.source === 'in_cluster') {
41+
return t('translation|In-cluster');
42+
}
43+
return 'Unknown';
44+
}
45+
2846
function ContextMenu({ cluster }: { cluster: Cluster }) {
2947
const { t } = useTranslation(['translation']);
3048
const history = useHistory();
3149
const dispatch = useDispatch();
3250
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
3351
const menuId = useId('context-menu');
3452
const [openConfirmDialog, setOpenConfirmDialog] = React.useState(false);
53+
const [openDeleteDynamicDialog, setOpenDeleteDynamicDialog] = React.useState(false);
3554

3655
function removeCluster(cluster: Cluster) {
3756
deleteCluster(cluster.name || '')
@@ -92,7 +111,8 @@ function ContextMenu({ cluster }: { cluster: Cluster }) {
92111
>
93112
<ListItemText>{t('translation|Settings')}</ListItemText>
94113
</MenuItem>
95-
{helpers.isElectron() && cluster.meta_data?.source === 'dynamic_cluster' && (
114+
115+
{helpers.isElectron() && (
96116
<MenuItem
97117
onClick={() => {
98118
setOpenConfirmDialog(true);
@@ -108,17 +128,52 @@ function ContextMenu({ cluster }: { cluster: Cluster }) {
108128
open={openConfirmDialog}
109129
handleClose={() => setOpenConfirmDialog(false)}
110130
onConfirm={() => {
111-
setOpenConfirmDialog(false);
112-
removeCluster(cluster);
131+
if (cluster.meta_data?.source !== 'dynamic_cluster') {
132+
setOpenDeleteDynamicDialog(true);
133+
} else {
134+
setOpenConfirmDialog(false);
135+
removeCluster(cluster);
136+
}
113137
}}
114138
title={t('translation|Delete Cluster')}
115139
description={t(
116-
'translation|Are you sure you want to remove the cluster "{{ clusterName }}"?',
140+
'translation|Are you sure you want to remove the cluster "{{ clusterName }}"? from {{ source }}',
117141
{
118142
clusterName: cluster.name,
143+
source: getOrigin(cluster),
119144
}
120145
)}
121146
/>
147+
148+
<ConfirmDialog
149+
open={openDeleteDynamicDialog}
150+
handleClose={() => setOpenDeleteDynamicDialog(false)}
151+
onConfirm={() => {
152+
setOpenDeleteDynamicDialog(false);
153+
removeCluster(cluster);
154+
}}
155+
title={t('translation|Delete Cluster')}
156+
description={
157+
<>
158+
{t(
159+
'translation|The cluster "{{ clusterName }}" is not a dynamic cluster from Headlamp, this cluster will be deleted from {{ source }}.',
160+
{
161+
clusterName: cluster.name,
162+
source: getOrigin(cluster),
163+
}
164+
)}
165+
166+
<>
167+
<Typography>Accept</Typography>
168+
<Checkbox
169+
checked={false}
170+
onChange={() => {}}
171+
inputProps={{ 'aria-label': 'primary checkbox' }}
172+
/>
173+
</>
174+
</>
175+
}
176+
/>
122177
</>
123178
);
124179
}
@@ -239,24 +294,6 @@ function HomeComponent(props: HomeComponentProps) {
239294
.sort();
240295
}
241296

242-
/**
243-
* Gets the origin of a cluster.
244-
*
245-
* @param cluster
246-
* @returns A description of where the cluster is picked up from: dynamic, in-cluster, or from a kubeconfig file.
247-
*/
248-
function getOrigin(cluster: Cluster): string {
249-
if (cluster.meta_data?.source === 'kubeconfig') {
250-
const kubeconfigPath = process.env.KUBECONFIG ?? '~/.kube/config';
251-
return `Kubeconfig: ${kubeconfigPath}`;
252-
} else if (cluster.meta_data?.source === 'dynamic_cluster') {
253-
return t('translation|Plugin');
254-
} else if (cluster.meta_data?.source === 'in_cluster') {
255-
return t('translation|In-cluster');
256-
}
257-
return 'Unknown';
258-
}
259-
260297
const memoizedComponent = React.useMemo(
261298
() => (
262299
<PageGrid>

frontend/src/components/common/ConfirmDialog.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { DialogTitle } from './Dialog';
99

1010
export interface ConfirmDialogProps extends MuiDialogProps {
1111
title: string;
12-
description: string;
12+
description: string | JSX.Element;
1313
onConfirm: () => void;
1414
handleClose: () => void;
1515
}

0 commit comments

Comments
 (0)