From ef3709ffe915221a6095204958772f5ce49753e4 Mon Sep 17 00:00:00 2001 From: Shaun Arman Date: Sun, 7 Jun 2026 17:39:07 -0500 Subject: [PATCH] fix(kube): bridge kubeconfig storage to in-memory cluster map and fix UI issues Resolves four bugs in the Kubernetes management interface: 1. **Cluster not found error** - commands/kube.rs::list_nodes (and all other kube resource commands) look up clusters from state.clusters (in-memory map) which was never populated from the kubeconfig_files table. Add a new connect_cluster_from_kubeconfig Tauri command that reads the encrypted kubeconfig from the DB, decrypts it, and inserts a ClusterClient into state.clusters. Wire it into KubernetesPage on initial load and cluster change so the in-memory map is always populated before any kube command runs. 2. **Dropdown selection has no effect** - same root cause as #1; activating a kubeconfig only updated the DB flag but never loaded the client into memory. handleClusterChange now calls connectClusterFromKubeconfigCmd after activation. 3. **GUID shown instead of cluster name** - ClusterOverview displayed the raw internal UUID as the page subtitle. Now accepts a clusterName prop (populated from kubeconfig.context) and renders that instead. ClusterDetails similarly changed to show kubeconfig.context in the header, not the UUID. 4. **Bell icon not clickable** - Hotbar bell button had no onClick handler. Add optional onNotifications / notificationCount props; badge count is now dynamic rather than hardcoded. KubernetesPage wires up a notifications dialog showing active cluster context and a link to the Events section. All changes follow TDD: failing tests written first, then implementation. --- src-tauri/src/commands/kube.rs | 38 ++++++++ src-tauri/src/lib.rs | 1 + src/components/Kubernetes/ClusterDetails.tsx | 4 +- src/components/Kubernetes/ClusterOverview.tsx | 7 +- src/components/Kubernetes/Hotbar.tsx | 19 ++-- src/lib/tauriCommands.ts | 3 + src/pages/Kubernetes/KubernetesPage.tsx | 46 +++++++++- tests/unit/ClusterDetails.test.tsx | 15 ++++ tests/unit/ClusterOverview.test.tsx | 22 +++++ tests/unit/Hotbar.test.tsx | 87 +++++++++++++++++++ tests/unit/kubernetesCommands.test.ts | 29 +++++++ 11 files changed, 262 insertions(+), 9 deletions(-) create mode 100644 tests/unit/Hotbar.test.tsx diff --git a/src-tauri/src/commands/kube.rs b/src-tauri/src/commands/kube.rs index 880cd1fb..b130d98a 100644 --- a/src-tauri/src/commands/kube.rs +++ b/src-tauri/src/commands/kube.rs @@ -156,6 +156,44 @@ fn extract_server_url(content: &str) -> Result { .ok_or_else(|| "Server URL not found in cluster".to_string()) } +/// Load a stored kubeconfig into the in-memory cluster map so all kube commands can use it. +/// +/// This bridges the kubeconfig_files table (encrypted storage) with the in-memory +/// state.clusters map that every kubernetes command requires. +#[tauri::command] +pub async fn connect_cluster_from_kubeconfig( + id: String, + state: State<'_, AppState>, +) -> Result<(), String> { + // Read name and encrypted content from DB + let (name, encrypted_content) = { + let db = state.db.lock().map_err(|e| e.to_string())?; + db.query_row( + "SELECT name, encrypted_content FROM kubeconfig_files WHERE id = ?1", + rusqlite::params![&id], + |row| Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?)), + ) + .map_err(|e| format!("Kubeconfig {id} not found in storage: {e}"))? + }; + + let content = crate::integrations::auth::decrypt_token(&encrypted_content)?; + let context = extract_context(&content)?; + let server_url = extract_server_url(&content).unwrap_or_default(); + + let client = ClusterClient::new( + id.clone(), + name, + context, + server_url, + Arc::new(content), + ); + + let mut clusters = state.clusters.lock().await; + clusters.insert(id, client); + + Ok(()) +} + #[tauri::command] pub async fn remove_cluster(id: String, state: State<'_, AppState>) -> Result<(), String> { // Check existence in memory BEFORE touching the DB diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index f20388a3..682e119a 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -179,6 +179,7 @@ pub fn run() { commands::shell::check_kubectl_installed, // Kubernetes Management commands::kube::add_cluster, + commands::kube::connect_cluster_from_kubeconfig, commands::kube::remove_cluster, commands::kube::list_clusters, commands::kube::start_port_forward, diff --git a/src/components/Kubernetes/ClusterDetails.tsx b/src/components/Kubernetes/ClusterDetails.tsx index 2bde2ddd..1f173c95 100644 --- a/src/components/Kubernetes/ClusterDetails.tsx +++ b/src/components/Kubernetes/ClusterDetails.tsx @@ -112,7 +112,9 @@ export function ClusterDetails({ clusterId }: ClusterDetailsProps) {

Cluster Details

-

Cluster ID: {clusterId}

+

+ {kubeconfig.context} +