diff --git a/src-tauri/src/commands/kube.rs b/src-tauri/src/commands/kube.rs index 33d8b162..01c63d25 100644 --- a/src-tauri/src/commands/kube.rs +++ b/src-tauri/src/commands/kube.rs @@ -6,8 +6,10 @@ use lazy_static::lazy_static; use regex::Regex; use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::process::Stdio; use std::sync::Arc; use tauri::State; +use tokio::io::AsyncWriteExt; use tokio::process::Command; use tracing::info; @@ -3609,44 +3611,257 @@ fn parse_hpas_json(json_str: &str) -> Result, S } #[tauri::command] -#[allow(unused_variables)] pub async fn cordon_node(cluster_id: String, node_name: String, state: State<'_, AppState>) -> Result<(), String> { - // Implementation similar to other management commands + let clusters = state.clusters.lock().await; + let cluster = clusters + .get(&cluster_id) + .ok_or_else(|| format!("Cluster {} not found", cluster_id))?; + + let kubeconfig_content = cluster.kubeconfig_content.as_ref(); + let context = &cluster.context; + + let temp_dir = std::env::temp_dir(); + let temp_path = temp_dir.join(format!("kubeconfig-{}-cordon.yaml", cluster_id)); + let _cleanup = TempFileCleanup(temp_path.clone()); + + std::fs::write(&temp_path, kubeconfig_content) + .map_err(|e| format!("Failed to write kubeconfig temp file: {e}"))?; + + let kubectl_path = locate_kubectl()?; + + let output = Command::new(kubectl_path) + .arg("cordon") + .arg(node_name) + .env("KUBECONFIG", temp_path.to_string_lossy().to_string()) + .env("KUBERNETES_CONTEXT", context) + .output() + .await + .map_err(|e| format!("Failed to execute kubectl: {e}"))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(stderr.to_string()); + } + Ok(()) } #[tauri::command] -#[allow(unused_variables)] pub async fn uncordon_node(cluster_id: String, node_name: String, state: State<'_, AppState>) -> Result<(), String> { - // Implementation similar to other management commands + let clusters = state.clusters.lock().await; + let cluster = clusters + .get(&cluster_id) + .ok_or_else(|| format!("Cluster {} not found", cluster_id))?; + + let kubeconfig_content = cluster.kubeconfig_content.as_ref(); + let context = &cluster.context; + + let temp_dir = std::env::temp_dir(); + let temp_path = temp_dir.join(format!("kubeconfig-{}-uncordon.yaml", cluster_id)); + let _cleanup = TempFileCleanup(temp_path.clone()); + + std::fs::write(&temp_path, kubeconfig_content) + .map_err(|e| format!("Failed to write kubeconfig temp file: {e}"))?; + + let kubectl_path = locate_kubectl()?; + + let output = Command::new(kubectl_path) + .arg("uncordon") + .arg(node_name) + .env("KUBECONFIG", temp_path.to_string_lossy().to_string()) + .env("KUBERNETES_CONTEXT", context) + .output() + .await + .map_err(|e| format!("Failed to execute kubectl: {e}"))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(stderr.to_string()); + } + Ok(()) } #[tauri::command] -#[allow(unused_variables)] pub async fn drain_node(cluster_id: String, node_name: String, state: State<'_, AppState>) -> Result<(), String> { - // Implementation similar to other management commands + let clusters = state.clusters.lock().await; + let cluster = clusters + .get(&cluster_id) + .ok_or_else(|| format!("Cluster {} not found", cluster_id))?; + + let kubeconfig_content = cluster.kubeconfig_content.as_ref(); + let context = &cluster.context; + + let temp_dir = std::env::temp_dir(); + let temp_path = temp_dir.join(format!("kubeconfig-{}-drain.yaml", cluster_id)); + let _cleanup = TempFileCleanup(temp_path.clone()); + + std::fs::write(&temp_path, kubeconfig_content) + .map_err(|e| format!("Failed to write kubeconfig temp file: {e}"))?; + + let kubectl_path = locate_kubectl()?; + + let output = Command::new(kubectl_path) + .arg("drain") + .arg(node_name) + .arg("--ignore-daemonsets") + .arg("--delete-emptydir-data") + .arg("--force") + .env("KUBECONFIG", temp_path.to_string_lossy().to_string()) + .env("KUBERNETES_CONTEXT", context) + .output() + .await + .map_err(|e| format!("Failed to execute kubectl: {e}"))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(stderr.to_string()); + } + Ok(()) } #[tauri::command] -#[allow(unused_variables)] pub async fn rollback_deployment(cluster_id: String, namespace: String, deployment_name: String, state: State<'_, AppState>) -> Result<(), String> { - // Implementation similar to other management commands + let clusters = state.clusters.lock().await; + let cluster = clusters + .get(&cluster_id) + .ok_or_else(|| format!("Cluster {} not found", cluster_id))?; + + let kubeconfig_content = cluster.kubeconfig_content.as_ref(); + let context = &cluster.context; + + let temp_dir = std::env::temp_dir(); + let temp_path = temp_dir.join(format!("kubeconfig-{}-rollback.yaml", cluster_id)); + let _cleanup = TempFileCleanup(temp_path.clone()); + + std::fs::write(&temp_path, kubeconfig_content) + .map_err(|e| format!("Failed to write kubeconfig temp file: {e}"))?; + + let kubectl_path = locate_kubectl()?; + + let output = Command::new(kubectl_path) + .arg("rollout") + .arg("undo") + .arg("deployment") + .arg(deployment_name) + .arg("-n") + .arg(namespace) + .env("KUBECONFIG", temp_path.to_string_lossy().to_string()) + .env("KUBERNETES_CONTEXT", context) + .output() + .await + .map_err(|e| format!("Failed to execute kubectl: {e}"))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(stderr.to_string()); + } + Ok(()) } #[tauri::command] -#[allow(unused_variables)] -pub async fn create_resource(cluster_id: String, namespace: String, resource_type: String, yaml_content: String, state: State<'_, AppState>) -> Result<(), String> { - // Implementation similar to other management commands +pub async fn create_resource(cluster_id: String, namespace: String, _resource_type: String, yaml_content: String, state: State<'_, AppState>) -> Result<(), String> { + let clusters = state.clusters.lock().await; + let cluster = clusters + .get(&cluster_id) + .ok_or_else(|| format!("Cluster {} not found", cluster_id))?; + + let kubeconfig_content = cluster.kubeconfig_content.as_ref(); + let context = &cluster.context; + + let temp_dir = std::env::temp_dir(); + let temp_path = temp_dir.join(format!("kubeconfig-{}-create.yaml", cluster_id)); + let _cleanup = TempFileCleanup(temp_path.clone()); + + std::fs::write(&temp_path, kubeconfig_content) + .map_err(|e| format!("Failed to write kubeconfig temp file: {e}"))?; + + let kubectl_path = locate_kubectl()?; + + let mut cmd = Command::new(kubectl_path); + cmd.arg("create") + .arg("-f") + .arg("-") + .arg("-n") + .arg(namespace) + .env("KUBECONFIG", temp_path.to_string_lossy().to_string()) + .env("KUBERNETES_CONTEXT", context) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let mut child = cmd.spawn() + .map_err(|e| format!("Failed to spawn kubectl: {e}"))?; + + if let Some(mut stdin) = child.stdin.take() { + stdin.write_all(yaml_content.as_bytes()) + .await + .map_err(|e| format!("Failed to write yaml to stdin: {e}"))?; + } + + let output = child.wait_with_output() + .await + .map_err(|e| format!("Failed to execute kubectl: {e}"))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(stderr.to_string()); + } + Ok(()) } #[tauri::command] -#[allow(unused_variables)] -pub async fn edit_resource(cluster_id: String, namespace: String, resource_type: String, resource_name: String, yaml_content: String, state: State<'_, AppState>) -> Result<(), String> { - // Implementation similar to other management commands +pub async fn edit_resource(cluster_id: String, namespace: String, _resource_type: String, _resource_name: String, yaml_content: String, state: State<'_, AppState>) -> Result<(), String> { + let clusters = state.clusters.lock().await; + let cluster = clusters + .get(&cluster_id) + .ok_or_else(|| format!("Cluster {} not found", cluster_id))?; + + let kubeconfig_content = cluster.kubeconfig_content.as_ref(); + let context = &cluster.context; + + let temp_dir = std::env::temp_dir(); + let temp_path = temp_dir.join(format!("kubeconfig-{}-edit.yaml", cluster_id)); + let _cleanup = TempFileCleanup(temp_path.clone()); + + std::fs::write(&temp_path, kubeconfig_content) + .map_err(|e| format!("Failed to write kubeconfig temp file: {e}"))?; + + let kubectl_path = locate_kubectl()?; + + let mut cmd = Command::new(kubectl_path); + cmd.arg("apply") + .arg("-f") + .arg("-") + .arg("-n") + .arg(namespace) + .env("KUBECONFIG", temp_path.to_string_lossy().to_string()) + .env("KUBERNETES_CONTEXT", context) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let mut child = cmd.spawn() + .map_err(|e| format!("Failed to spawn kubectl: {e}"))?; + + if let Some(mut stdin) = child.stdin.take() { + stdin.write_all(yaml_content.as_bytes()) + .await + .map_err(|e| format!("Failed to write yaml to stdin: {e}"))?; + } + + let output = child.wait_with_output() + .await + .map_err(|e| format!("Failed to execute kubectl: {e}"))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(stderr.to_string()); + } + Ok(()) } diff --git a/src/lib/tauriCommands.ts b/src/lib/tauriCommands.ts index 6e9928bf..1bbb53c4 100644 --- a/src/lib/tauriCommands.ts +++ b/src/lib/tauriCommands.ts @@ -947,3 +947,213 @@ export const deleteResourceCmd = (clusterId: string, resourceType: string, names export const execPodCmd = (clusterId: string, namespace: string, podName: string, containerName: string, command: string, shell?: string) => invoke("exec_pod", { clusterId, namespace, podName, containerName, shell, command }); + +// ─── Additional Kubernetes Resource Discovery Types ─────────────────────────── + +export interface ReplicaSetInfo { + name: string; + namespace: string; + replicas: number; + ready: string; + age: string; + labels: Record; +} + +export interface JobInfo { + name: string; + namespace: string; + completions: string; + duration: string; + age: string; + labels: Record; +} + +export interface CronJobInfo { + name: string; + namespace: string; + schedule: string; + active: number; + last_schedule: string; + age: string; + labels: Record; +} + +export interface ConfigMapInfo { + name: string; + namespace: string; + data_keys: number; + age: string; +} + +export interface SecretInfo { + name: string; + namespace: string; + type: string; + data_keys: number; + age: string; +} + +export interface NodeInfo { + name: string; + status: string; + roles: string; + version: string; + internal_ip: string; + external_ip?: string; + os_image: string; + kernel_version: string; + kubelet_version: string; + age: string; +} + +export interface EventInfo { + name: string; + namespace: string; + event_type: string; + reason: string; + object: string; + count: number; + first_seen: string; + last_seen: string; + message: string; +} + +export interface IngressInfo { + name: string; + namespace: string; + class?: string; + host: string; + addresses: string[]; + age: string; +} + +export interface PersistentVolumeClaimInfo { + name: string; + namespace: string; + status: string; + volume: string; + capacity: string; + access_modes: string[]; + age: string; +} + +export interface PersistentVolumeInfo { + name: string; + status: string; + capacity: string; + access_modes: string[]; + reclaim_policy: string; + storage_class: string; + age: string; +} + +export interface ServiceAccountInfo { + name: string; + namespace: string; + secrets: number; + age: string; +} + +export interface RoleInfo { + name: string; + namespace: string; + age: string; +} + +export interface ClusterRoleInfo { + name: string; + age: string; +} + +export interface RoleBindingInfo { + name: string; + namespace: string; + role: string; + age: string; +} + +export interface ClusterRoleBindingInfo { + name: string; + cluster_role: string; + age: string; +} + +export interface HorizontalPodAutoscalerInfo { + name: string; + namespace: string; + min_replicas: number; + max_replicas: number; + current_replicas: number; + desired_replicas: number; + age: string; +} + +// ─── Additional Kubernetes Resource Discovery Commands ──────────────────────── + +export const listReplicasetsCmd = (clusterId: string, namespace: string) => + invoke("list_replicasets", { clusterId, namespace }); + +export const listJobsCmd = (clusterId: string, namespace: string) => + invoke("list_jobs", { clusterId, namespace }); + +export const listCronjobsCmd = (clusterId: string, namespace: string) => + invoke("list_cronjobs", { clusterId, namespace }); + +export const listConfigmapsCmd = (clusterId: string, namespace: string) => + invoke("list_configmaps", { clusterId, namespace }); + +export const listSecretsCmd = (clusterId: string, namespace: string) => + invoke("list_secrets", { clusterId, namespace }); + +export const listNodesCmd = (clusterId: string) => + invoke("list_nodes", { clusterId }); + +export const listEventsCmd = (clusterId: string, namespace?: string) => + invoke("list_events", { clusterId, namespace }); + +export const listIngressesCmd = (clusterId: string, namespace: string) => + invoke("list_ingresses", { clusterId, namespace }); + +export const listPersistentvolumeclaimsCmd = (clusterId: string, namespace: string) => + invoke("list_persistentvolumeclaims", { clusterId, namespace }); + +export const listPersistentvolumesCmd = (clusterId: string) => + invoke("list_persistentvolumes", { clusterId }); + +export const listServiceaccountsCmd = (clusterId: string, namespace: string) => + invoke("list_serviceaccounts", { clusterId, namespace }); + +export const listRolesCmd = (clusterId: string, namespace: string) => + invoke("list_roles", { clusterId, namespace }); + +export const listClusterrolesCmd = (clusterId: string) => + invoke("list_clusterroles", { clusterId }); + +export const listRolebindingsCmd = (clusterId: string, namespace: string) => + invoke("list_rolebindings", { clusterId, namespace }); + +export const listClusterrolebindingsCmd = (clusterId: string) => + invoke("list_clusterrolebindings", { clusterId }); + +export const listHorizontalpodautoscalersCmd = (clusterId: string, namespace: string) => + invoke("list_horizontalpodautoscalers", { clusterId, namespace }); + +// ─── Additional Kubernetes Resource Management Commands ─────────────────────── + +export const cordonNodeCmd = (clusterId: string, nodeName: string) => + invoke("cordon_node", { clusterId, nodeName }); + +export const uncordonNodeCmd = (clusterId: string, nodeName: string) => + invoke("uncordon_node", { clusterId, nodeName }); + +export const drainNodeCmd = (clusterId: string, nodeName: string) => + invoke("drain_node", { clusterId, nodeName }); + +export const rollbackDeploymentCmd = (clusterId: string, namespace: string, deploymentName: string) => + invoke("rollback_deployment", { clusterId, namespace, deploymentName }); + +export const createResourceCmd = (clusterId: string, namespace: string, resourceType: string, yamlContent: string) => + invoke("create_resource", { clusterId, namespace, resourceType, yamlContent }); + +export const editResourceCmd = (clusterId: string, namespace: string, resourceType: string, resourceName: string, yamlContent: string) => + invoke("edit_resource", { clusterId, namespace, resourceType, resourceName, yamlContent });