From b8fb9abb546d9bf6a8919e3ca83898ec3a223cc9 Mon Sep 17 00:00:00 2001 From: Kautilya Tripathi Date: Mon, 2 Sep 2024 16:16:50 +0530 Subject: [PATCH 1/2] backend: Fix return of renaming stateless clusters The statement was not returning due to which indexDB was not getting updated. Signed-off-by: Kautilya Tripathi --- backend/cmd/headlamp.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/cmd/headlamp.go b/backend/cmd/headlamp.go index 88da814178..d271c874dd 100644 --- a/backend/cmd/headlamp.go +++ b/backend/cmd/headlamp.go @@ -1475,6 +1475,8 @@ func (c *HeadlampConfig) renameCluster(w http.ResponseWriter, r *http.Request) { if reqBody.Stateless { // For stateless clusters we just need to remove cluster from cache c.handleStatelessClusterRename(w, r, clusterName) + + return } // Get path of kubeconfig from source From c2910589a711203f62a53378c351e908b460f85e Mon Sep 17 00:00:00 2001 From: Kautilya Tripathi Date: Mon, 2 Sep 2024 16:17:41 +0530 Subject: [PATCH 2/2] frontend: Add check for adding same name clusters Earlier user were able to submit clusters with same name to the backend, due to which it was getting overwritten on the proxy. Even if the cluster was shown in frontend the backend did not knew about the duplicate cluster. kubectl throws the error of adding duplicate context name. We are adding feature for the same. If now user try to add a context which has the same name as earlier context the app with throw an error and ask them to change the context name. Fixes: #2236 Signed-off-by: Kautilya Tripathi --- .../components/cluster/KubeConfigLoader.tsx | 32 +++++++++++++++++-- frontend/src/i18n/locales/de/translation.json | 2 ++ frontend/src/i18n/locales/en/translation.json | 2 ++ frontend/src/i18n/locales/es/translation.json | 2 ++ frontend/src/i18n/locales/fr/translation.json | 2 ++ frontend/src/i18n/locales/pt/translation.json | 2 ++ frontend/src/stateless/index.ts | 18 ++++++----- 7 files changed, 50 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/cluster/KubeConfigLoader.tsx b/frontend/src/components/cluster/KubeConfigLoader.tsx index fd2440c286..a2def7b703 100644 --- a/frontend/src/components/cluster/KubeConfigLoader.tsx +++ b/frontend/src/components/cluster/KubeConfigLoader.tsx @@ -10,6 +10,7 @@ import { useDropzone } from 'react-dropzone'; import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; import { useHistory } from 'react-router-dom'; +import { useClustersConf } from '../../lib/k8s'; import { setCluster } from '../../lib/k8s/apiProxy'; import { setStatelessConfig } from '../../redux/configSlice'; import { DialogTitle } from '../common/Dialog'; @@ -105,6 +106,7 @@ const WideButton = styled(Button)({ const enum Step { LoadKubeConfig, SelectClusters, + ValidateKubeConfig, ConfigureClusters, Success, } @@ -120,6 +122,7 @@ function KubeConfigLoader() { currentContext: '', }); const [selectedClusters, setSelectedClusters] = useState([]); + const configuredClusters = useClustersConf(); // Get already configured clusters useEffect(() => { if (fileContent.contexts.length > 0) { @@ -130,9 +133,27 @@ function KubeConfigLoader() { }, [fileContent]); useEffect(() => { + if (state === Step.ValidateKubeConfig) { + const alreadyConfiguredClusters = selectedClusters.filter( + clusterName => configuredClusters && configuredClusters[clusterName] + ); + + if (alreadyConfiguredClusters.length > 0) { + setError( + t( + 'translation|Duplicate cluster: {{ clusterNames }} in the list. Please edit the context name.', + { + clusterNames: alreadyConfiguredClusters.join(', '), + } + ) + ); + setState(Step.SelectClusters); + } else { + setState(Step.ConfigureClusters); + } + } if (state === Step.ConfigureClusters) { function loadClusters() { - //@todo: We need to check if the cluster is already configured. const selectedClusterConfig = configWithSelectedClusters(fileContent, selectedClusters); setCluster({ kubeconfig: btoa(yaml.dump(selectedClusterConfig)) }) .then(res => { @@ -295,7 +316,7 @@ function KubeConfigLoader() { variant="contained" color="primary" onClick={() => { - setState(Step.ConfigureClusters); + setState(Step.ValidateKubeConfig); }} disabled={selectedClusters.length === 0} > @@ -318,6 +339,13 @@ function KubeConfigLoader() { ) : null} ); + case Step.ValidateKubeConfig: + return ( + + {t('translation|Validating selected clusters')} + + + ); case Step.ConfigureClusters: return ( diff --git a/frontend/src/i18n/locales/de/translation.json b/frontend/src/i18n/locales/de/translation.json index 558f436ed1..c77f36bb64 100644 --- a/frontend/src/i18n/locales/de/translation.json +++ b/frontend/src/i18n/locales/de/translation.json @@ -110,6 +110,7 @@ "Current//context:cluster": "Aktuell", "Choose cluster": "Cluster auswählen", "Add Cluster": "Cluster hinzufügen", + "Duplicate cluster: {{ clusterNames }} in the list. Please edit the context name.": "", "Error setting up clusters, please load a valid kubeconfig file": "Fehler beim Einrichten der Cluster. Bitte laden Sie eine korrekte kubeconfig-Datei", "Couldn't read kubeconfig file": "Konnte die kubeconfig-Datei nicht lesen", "No clusters found!": "", @@ -119,6 +120,7 @@ "Choose file": "Datei auswählen", "Select clusters": "Cluster auswählen", "Next": "Weiter", + "Validating selected clusters": "", "Setting up clusters": "Einrichten der Cluster", "Clusters successfully set up!": "Cluster erfolgreich eingerichtet!", "Finish": "Beenden", diff --git a/frontend/src/i18n/locales/en/translation.json b/frontend/src/i18n/locales/en/translation.json index 0d8a529ef6..721b0b6b99 100644 --- a/frontend/src/i18n/locales/en/translation.json +++ b/frontend/src/i18n/locales/en/translation.json @@ -110,6 +110,7 @@ "Current//context:cluster": "Current", "Choose cluster": "Choose cluster", "Add Cluster": "Add Cluster", + "Duplicate cluster: {{ clusterNames }} in the list. Please edit the context name.": "Duplicate cluster: {{ clusterNames }} in the list. Please edit the context name.", "Error setting up clusters, please load a valid kubeconfig file": "Error setting up clusters, please load a valid kubeconfig file", "Couldn't read kubeconfig file": "Couldn't read kubeconfig file", "No clusters found!": "No clusters found!", @@ -119,6 +120,7 @@ "Choose file": "Choose file", "Select clusters": "Select clusters", "Next": "Next", + "Validating selected clusters": "Validating selected clusters", "Setting up clusters": "Setting up clusters", "Clusters successfully set up!": "Clusters successfully set up!", "Finish": "Finish", diff --git a/frontend/src/i18n/locales/es/translation.json b/frontend/src/i18n/locales/es/translation.json index 6cde511210..bd2ecfd616 100644 --- a/frontend/src/i18n/locales/es/translation.json +++ b/frontend/src/i18n/locales/es/translation.json @@ -110,6 +110,7 @@ "Current//context:cluster": "Actuales", "Choose cluster": "Elegir cluster", "Add Cluster": "Añadir cluster", + "Duplicate cluster: {{ clusterNames }} in the list. Please edit the context name.": "", "Error setting up clusters, please load a valid kubeconfig file": "Error al configurar los clusters, por favor cargue un archivo kubeconfig válido", "Couldn't read kubeconfig file": "No se pudo leer el archivo kubeconfig", "No clusters found!": "", @@ -119,6 +120,7 @@ "Choose file": "Elija el archivo", "Select clusters": "Seleccione los clusters", "Next": "Siguiente", + "Validating selected clusters": "", "Setting up clusters": "Configurando los clusters", "Clusters successfully set up!": "¡Clusters configurados con éxito!", "Finish": "Finalizar", diff --git a/frontend/src/i18n/locales/fr/translation.json b/frontend/src/i18n/locales/fr/translation.json index 2f0a2405ea..742b59b9bb 100644 --- a/frontend/src/i18n/locales/fr/translation.json +++ b/frontend/src/i18n/locales/fr/translation.json @@ -110,6 +110,7 @@ "Current//context:cluster": "Actuel", "Choose cluster": "Choisir un cluster", "Add Cluster": "Ajouter un cluster", + "Duplicate cluster: {{ clusterNames }} in the list. Please edit the context name.": "", "Error setting up clusters, please load a valid kubeconfig file": "Erreur lors de la configuration des clusters, veuillez charger un fichier kubeconfig valide", "Couldn't read kubeconfig file": "Impossible de lire le fichier kubeconfig", "No clusters found!": "", @@ -119,6 +120,7 @@ "Choose file": "Choisir un fichier", "Select clusters": "Sélectionner des clusters", "Next": "Suivant", + "Validating selected clusters": "", "Setting up clusters": "Configuration des clusters", "Clusters successfully set up!": "Clusters configurés avec succès !", "Finish": "Terminer", diff --git a/frontend/src/i18n/locales/pt/translation.json b/frontend/src/i18n/locales/pt/translation.json index 3b8f801a61..441781b762 100644 --- a/frontend/src/i18n/locales/pt/translation.json +++ b/frontend/src/i18n/locales/pt/translation.json @@ -110,6 +110,7 @@ "Current//context:cluster": "Actual", "Choose cluster": "Escolha o cluster", "Add Cluster": "Adicionar Cluster", + "Duplicate cluster: {{ clusterNames }} in the list. Please edit the context name.": "", "Error setting up clusters, please load a valid kubeconfig file": "Erro ao configurar clusters, por favor carregue um ficheiro kubeconfig válido", "Couldn't read kubeconfig file": "Não foi possível ler o ficheiro kubeconfig", "No clusters found!": "", @@ -119,6 +120,7 @@ "Choose file": "Escolha o ficheiro", "Select clusters": "Selecione os clusters", "Next": "Seguinte", + "Validating selected clusters": "", "Setting up clusters": "A configurar clusters", "Clusters successfully set up!": "Clusters configurados com sucesso!", "Finish": "Terminar", diff --git a/frontend/src/stateless/index.ts b/frontend/src/stateless/index.ts index d27978de8c..dcae5569ec 100644 --- a/frontend/src/stateless/index.ts +++ b/frontend/src/stateless/index.ts @@ -247,8 +247,8 @@ export function findKubeconfigByClusterName(clusterName: string): Promise cluster.name === clusterName + const matchingKubeconfig = parsedKubeconfig.contexts.find( + context => context.name === clusterName ); if (matchingKubeconfig || matchingContext) { @@ -437,9 +437,12 @@ export async function deleteClusterKubeconfig(clusterName: string): Promise cluster.name === clusterName + // Find the context with the matching cluster name or custom name in headlamp_info + const matchingKubeconfig = parsedKubeconfig.contexts.find( + context => + context.name === clusterName || + context.context.extensions?.find(extension => extension.name === 'headlamp_info') + ?.extension.customName === clusterName ); if (matchingKubeconfig) { @@ -489,11 +492,10 @@ export function updateStatelessClusterKubeconfig( const request = indexedDB.open('kubeconfigs', 1) as any; // Parse the kubeconfig from base64 const parsedKubeconfig = jsyaml.load(atob(kubeconfig)) as KubeconfigObject; - // Find the context with the matching cluster name or custom name in headlamp_info const matchingContext = parsedKubeconfig.contexts.find( context => - context.context.cluster === clusterName || + context.name === clusterName || context.context.extensions?.find(extension => extension.name === 'headlamp_info') ?.extension.customName === clusterName ); @@ -502,7 +504,7 @@ export function updateStatelessClusterKubeconfig( const extensions = matchingContext.context.extensions || []; const headlampExtension = extensions.find(extension => extension.name === 'headlamp_info'); - if (matchingContext.context.cluster === clusterName) { + if (matchingContext.name === clusterName) { // Push the new extension if the cluster name matches extensions.push({ extension: {