2026-06-06 23:32:08 +00:00
|
|
|
use crate::kube::portforward::{PortForwardSession, PortForwardSessionConfig};
|
2026-06-06 19:00:09 +00:00
|
|
|
use crate::kube::ClusterClient;
|
2026-06-06 20:14:04 +00:00
|
|
|
use crate::shell::kubectl::locate_kubectl;
|
2026-06-06 16:41:23 +00:00
|
|
|
use crate::state::AppState;
|
2026-06-06 23:32:08 +00:00
|
|
|
use lazy_static::lazy_static;
|
2026-06-06 21:23:00 +00:00
|
|
|
use regex::Regex;
|
2026-06-06 16:41:23 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
use serde_json::Value;
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
use std::process::Stdio;
|
2026-06-06 18:28:03 +00:00
|
|
|
use std::sync::Arc;
|
2026-06-06 16:41:23 +00:00
|
|
|
use tauri::State;
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
use tokio::io::AsyncWriteExt;
|
2026-06-06 20:14:04 +00:00
|
|
|
use tokio::process::Command;
|
2026-06-06 21:23:00 +00:00
|
|
|
use tracing::info;
|
2026-06-06 16:41:23 +00:00
|
|
|
|
2026-06-06 23:32:08 +00:00
|
|
|
// Regex pattern for Kubernetes resource names - cached for performance
|
|
|
|
|
lazy_static! {
|
|
|
|
|
static ref NAME_PATTERN_REGEX: Regex = Regex::new(r"^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$").unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
feat(k8s): implement clean-room Kubernetes management GUI
- Backend: kube module with ClusterClient, PortForwardSession, RefreshRegistry
- 7 Tauri IPC commands: add_cluster, remove_cluster, list_clusters, start_port_forward, stop_port_forward, list_port_forwards, delete_port_forward, shutdown_port_forwards
- AppState extended with clusters, port_forwards, refresh_registry fields
- Version bumped to 1.1.0 in Cargo.toml and package.json
- Auto-tag workflow updated to mark releases as draft (pre-release)
- Buy Me A Coffee section added to README.md
- Fixed changelog workflow to only include current tag commits
- Proper kubeconfig YAML parsing with extract_context and extract_server_url
- Added kubeconfig content storage in ClusterClient
- Updated PortForwardSession to include cluster_name
- Frontend GUI components: ClusterList, PortForwardList, AddClusterModal, PortForwardForm, KubernetesPage
- TypeScript types and IPC commands for Kubernetes management
- Unit tests for Kubernetes IPC commands (6 tests)
- All 332 Rust tests passing
- All 98 frontend tests passing
- TypeScript type checks passing
- Project builds successfully in release mode
- Committed and pushed to feature/kubernetes-management branch
- Command injection vulnerability fixed with regex validation and max length check (253 chars)
- stop_port_forward and shutdown_port_forwards properly kill kubectl child processes via async child management
- Temp file cleanup implemented with RAII TempFileCleanup struct created before std::fs::write
- discover_pods now parses actual kubectl JSON output
- ChildWaitHandle implemented with background task for waiting on kubectl child
- PortForwardSession uses Arc<TokioMutex<Option<Child>>> for async-safe child management
- Port-forward uses kubectl's dynamic port binding (0) instead of TcpListener
- Added shutdown_port_forwards command for app shutdown cleanup
- Added cleanup effect in App.tsx to call shutdownPortForwardsCmd on unmount
- Database CRUD operations for clusters and port_forwards added to db.rs
- validate_resource_name uses lazy_static! for cached Regex to prevent ReDoS
- Cluster struct updated to store kubeconfig_content directly instead of kubeconfig_id
- Cluster model in db/models.rs updated to use kubeconfig_content field
- load_clusters and load_port_forwards commands registered in lib.rs
- Temp file cleanup moved to background task in ChildWaitHandle to ensure cleanup after kubectl completes
- Unused child_id field removed from ChildWaitHandle
- Command validation moved to beginning of start_port_forward before any operations
- Fixed lint errors: removed unused imports, fixed React hooks order, updated type annotations
- Updated eslint.config.js to properly configure file patterns
2026-06-07 01:27:39 +00:00
|
|
|
struct TempFileCleanup(std::path::PathBuf);
|
|
|
|
|
impl Drop for TempFileCleanup {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
let _ = std::fs::remove_file(&self.0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 16:41:23 +00:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ClusterInfo {
|
|
|
|
|
pub id: String,
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub context: String,
|
|
|
|
|
pub cluster_url: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct PortForwardRequest {
|
|
|
|
|
pub cluster_id: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub pod: String,
|
|
|
|
|
pub container_port: u16,
|
2026-06-06 21:23:00 +00:00
|
|
|
/// Optional: Local port to bind to. If 0, kubectl will allocate dynamically.
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub local_port: u16,
|
2026-06-06 16:41:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct PortForwardResponse {
|
|
|
|
|
pub id: String,
|
|
|
|
|
pub cluster_id: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub pod: String,
|
2026-06-06 21:23:00 +00:00
|
|
|
pub container_ports: Vec<u16>,
|
|
|
|
|
pub local_ports: Vec<u16>,
|
2026-06-06 16:41:23 +00:00
|
|
|
pub status: String,
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 20:14:04 +00:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct PodInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub status: String,
|
|
|
|
|
pub ready: String,
|
|
|
|
|
pub age: String,
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
pub containers: Vec<String>,
|
2026-06-06 20:14:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ClusterConnectionStatus {
|
|
|
|
|
pub status: ClusterConnectionState,
|
|
|
|
|
pub context: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
#[serde(tag = "type")]
|
|
|
|
|
pub enum ClusterConnectionState {
|
|
|
|
|
Connected,
|
|
|
|
|
Disconnected { error: String },
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 16:41:23 +00:00
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn add_cluster(
|
|
|
|
|
id: String,
|
|
|
|
|
name: String,
|
|
|
|
|
kubeconfig_content: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<ClusterInfo, String> {
|
2026-06-06 18:17:56 +00:00
|
|
|
if kubeconfig_content.trim().is_empty() {
|
|
|
|
|
return Err("Kubeconfig content cannot be empty".to_string());
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 16:41:23 +00:00
|
|
|
let context = extract_context(&kubeconfig_content)?;
|
|
|
|
|
let server_url = extract_server_url(&kubeconfig_content)?;
|
|
|
|
|
|
2026-06-06 18:28:03 +00:00
|
|
|
let kubeconfig_arc = Arc::new(kubeconfig_content.clone());
|
2026-06-06 16:41:23 +00:00
|
|
|
let client = ClusterClient::new(
|
|
|
|
|
id.clone(),
|
|
|
|
|
name.clone(),
|
|
|
|
|
context.clone(),
|
|
|
|
|
server_url.clone(),
|
2026-06-06 18:28:03 +00:00
|
|
|
kubeconfig_arc,
|
2026-06-06 16:41:23 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let mut clusters = state.clusters.lock().await;
|
|
|
|
|
clusters.insert(id.clone(), client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(ClusterInfo {
|
|
|
|
|
id,
|
|
|
|
|
name,
|
|
|
|
|
context,
|
|
|
|
|
cluster_url: server_url,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 18:17:56 +00:00
|
|
|
fn extract_context(content: &str) -> Result<String, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let value: serde_yaml::Value =
|
2026-06-06 18:30:35 +00:00
|
|
|
serde_yaml::from_str(content).map_err(|e| format!("Invalid kubeconfig YAML: {}", e))?;
|
2026-06-06 18:17:56 +00:00
|
|
|
|
2026-06-08 00:40:53 +00:00
|
|
|
// Prefer current-context — this is what kubectl uses by default and what the
|
|
|
|
|
// user intends when they upload their kubeconfig. Falling back to contexts[0]
|
|
|
|
|
// picks the wrong entry when the file has multiple contexts.
|
|
|
|
|
if let Some(current) = value.get("current-context").and_then(|c| c.as_str()) {
|
|
|
|
|
if !current.is_empty() {
|
|
|
|
|
return Ok(current.to_string());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No current-context set — fall back to the first context in the list
|
2026-06-06 18:17:56 +00:00
|
|
|
let contexts = value
|
|
|
|
|
.get("contexts")
|
|
|
|
|
.and_then(|c| c.as_sequence())
|
|
|
|
|
.ok_or("Missing 'contexts' field in kubeconfig")?;
|
|
|
|
|
|
|
|
|
|
if contexts.is_empty() {
|
|
|
|
|
return Err("No contexts found in kubeconfig".to_string());
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-08 00:40:53 +00:00
|
|
|
contexts[0]
|
|
|
|
|
.get("name")
|
|
|
|
|
.and_then(|n| n.as_str())
|
2026-06-06 18:17:56 +00:00
|
|
|
.map(|s| s.to_string())
|
|
|
|
|
.ok_or_else(|| "Context name not found".to_string())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn extract_server_url(content: &str) -> Result<String, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let value: serde_yaml::Value =
|
2026-06-06 18:30:35 +00:00
|
|
|
serde_yaml::from_str(content).map_err(|e| format!("Invalid kubeconfig YAML: {}", e))?;
|
2026-06-06 18:17:56 +00:00
|
|
|
|
|
|
|
|
let clusters = value
|
|
|
|
|
.get("clusters")
|
|
|
|
|
.and_then(|c| c.as_sequence())
|
|
|
|
|
.ok_or("Missing 'clusters' field in kubeconfig")?;
|
|
|
|
|
|
|
|
|
|
if clusters.is_empty() {
|
|
|
|
|
return Err("No clusters found in kubeconfig".to_string());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let cluster = &clusters[0];
|
|
|
|
|
let server = cluster
|
|
|
|
|
.get("cluster")
|
|
|
|
|
.and_then(|c| c.get("server"))
|
|
|
|
|
.and_then(|s| s.as_str());
|
|
|
|
|
|
|
|
|
|
server
|
|
|
|
|
.map(|s| s.to_string())
|
|
|
|
|
.ok_or_else(|| "Server URL not found in cluster".to_string())
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-07 22:39:07 +00:00
|
|
|
/// 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();
|
|
|
|
|
|
fix(classifier): fix 3 safety bugs, extract const arrays, make tier UI dynamic
Bug 1 — Dead multi-word tier3 entries / missing single-token commands
parse_single_command() extracts only the first token as `command`, so
multi-word entries like "kill -9", "init 0", "service stop" in the tier3
array never matched. Adding the single-token forms "kill", "pkill",
"killall", "init" to TIER3_COMMANDS ensures these commands are always
denied. Removed all dead multi-word entries.
Bug 2 — systemctl Tier 1 special case was dead code
systemctl was not in tier1_general, so the block that was supposed to
auto-execute `systemctl status` never ran. Moved systemctl handling into
its own block (TIER1_SYSTEMCTL_SUBCOMMANDS / TIER2_SYSTEMCTL_SUBCOMMANDS)
evaluated before the general tier checks. status, is-active, is-enabled,
list-units, list-unit-files → Tier 1; all others → Tier 2.
Bug 3 — ldapmodify / ldapdelete / ldapadd misclassified as Tier 1
Both appeared in the old tier1_general and tier2_general arrays; the tier1
check ran first, so LDAP write operations auto-executed. Removed them from
tier1. ldapsearch (read-only) remains Tier 1.
Dynamic Safety Architecture UI
Extracted all tier classification arrays to module-level pub const slices
(TIER3_COMMANDS, TIER1_KUBECTL_SUBCOMMANDS, etc.) so both the classifier
logic and a new get_classifier_rules() Tauri command share a single source
of truth. ShellExecution.tsx now calls getClassifierRulesCmd() on mount and
renders the actual command lists in collapsible per-tier cards — any change
to the const arrays is automatically reflected in the UI with no manual
documentation update needed.
Also fixes the cargo fmt CI failure introduced in the previous commit
(ClusterClient::new call reformatted to a single line).
2026-06-07 23:15:42 +00:00
|
|
|
let client = ClusterClient::new(id.clone(), name, context, server_url, Arc::new(content));
|
2026-06-07 22:39:07 +00:00
|
|
|
|
|
|
|
|
let mut clusters = state.clusters.lock().await;
|
|
|
|
|
clusters.insert(id, client);
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 16:41:23 +00:00
|
|
|
#[tauri::command]
|
2026-06-06 17:04:21 +00:00
|
|
|
pub async fn remove_cluster(id: String, state: State<'_, AppState>) -> Result<(), String> {
|
feat(k8s): implement clean-room Kubernetes management GUI
- Backend: kube module with ClusterClient, PortForwardSession, RefreshRegistry
- 7 Tauri IPC commands: add_cluster, remove_cluster, list_clusters, start_port_forward, stop_port_forward, list_port_forwards, delete_port_forward, shutdown_port_forwards
- AppState extended with clusters, port_forwards, refresh_registry fields
- Version bumped to 1.1.0 in Cargo.toml and package.json
- Auto-tag workflow updated to mark releases as draft (pre-release)
- Buy Me A Coffee section added to README.md
- Fixed changelog workflow to only include current tag commits
- Proper kubeconfig YAML parsing with extract_context and extract_server_url
- Added kubeconfig content storage in ClusterClient
- Updated PortForwardSession to include cluster_name
- Frontend GUI components: ClusterList, PortForwardList, AddClusterModal, PortForwardForm, KubernetesPage
- TypeScript types and IPC commands for Kubernetes management
- Unit tests for Kubernetes IPC commands (6 tests)
- All 332 Rust tests passing
- All 98 frontend tests passing
- TypeScript type checks passing
- Project builds successfully in release mode
- Committed and pushed to feature/kubernetes-management branch
- Command injection vulnerability fixed with regex validation and max length check (253 chars)
- stop_port_forward and shutdown_port_forwards properly kill kubectl child processes via async child management
- Temp file cleanup implemented with RAII TempFileCleanup struct created before std::fs::write
- discover_pods now parses actual kubectl JSON output
- ChildWaitHandle implemented with background task for waiting on kubectl child
- PortForwardSession uses Arc<TokioMutex<Option<Child>>> for async-safe child management
- Port-forward uses kubectl's dynamic port binding (0) instead of TcpListener
- Added shutdown_port_forwards command for app shutdown cleanup
- Added cleanup effect in App.tsx to call shutdownPortForwardsCmd on unmount
- Database CRUD operations for clusters and port_forwards added to db.rs
- validate_resource_name uses lazy_static! for cached Regex to prevent ReDoS
- Cluster struct updated to store kubeconfig_content directly instead of kubeconfig_id
- Cluster model in db/models.rs updated to use kubeconfig_content field
- load_clusters and load_port_forwards commands registered in lib.rs
- Temp file cleanup moved to background task in ChildWaitHandle to ensure cleanup after kubectl completes
- Unused child_id field removed from ChildWaitHandle
- Command validation moved to beginning of start_port_forward before any operations
- Fixed lint errors: removed unused imports, fixed React hooks order, updated type annotations
- Updated eslint.config.js to properly configure file patterns
2026-06-07 01:27:39 +00:00
|
|
|
// Check existence in memory BEFORE touching the DB
|
|
|
|
|
let exists = {
|
|
|
|
|
let clusters = state.clusters.lock().await;
|
|
|
|
|
clusters.contains_key(&id)
|
|
|
|
|
};
|
|
|
|
|
if !exists {
|
|
|
|
|
return Err(format!("Cluster {id} not found"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Safe to delete from DB now
|
2026-06-07 00:29:42 +00:00
|
|
|
{
|
|
|
|
|
let db = state.db.lock().map_err(|e| e.to_string())?;
|
|
|
|
|
db.execute("DELETE FROM clusters WHERE id = ?1", [&id])
|
|
|
|
|
.map_err(|e| format!("Failed to delete cluster: {e}"))?;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 16:41:23 +00:00
|
|
|
let mut clusters = state.clusters.lock().await;
|
feat(k8s): implement clean-room Kubernetes management GUI
- Backend: kube module with ClusterClient, PortForwardSession, RefreshRegistry
- 7 Tauri IPC commands: add_cluster, remove_cluster, list_clusters, start_port_forward, stop_port_forward, list_port_forwards, delete_port_forward, shutdown_port_forwards
- AppState extended with clusters, port_forwards, refresh_registry fields
- Version bumped to 1.1.0 in Cargo.toml and package.json
- Auto-tag workflow updated to mark releases as draft (pre-release)
- Buy Me A Coffee section added to README.md
- Fixed changelog workflow to only include current tag commits
- Proper kubeconfig YAML parsing with extract_context and extract_server_url
- Added kubeconfig content storage in ClusterClient
- Updated PortForwardSession to include cluster_name
- Frontend GUI components: ClusterList, PortForwardList, AddClusterModal, PortForwardForm, KubernetesPage
- TypeScript types and IPC commands for Kubernetes management
- Unit tests for Kubernetes IPC commands (6 tests)
- All 332 Rust tests passing
- All 98 frontend tests passing
- TypeScript type checks passing
- Project builds successfully in release mode
- Committed and pushed to feature/kubernetes-management branch
- Command injection vulnerability fixed with regex validation and max length check (253 chars)
- stop_port_forward and shutdown_port_forwards properly kill kubectl child processes via async child management
- Temp file cleanup implemented with RAII TempFileCleanup struct created before std::fs::write
- discover_pods now parses actual kubectl JSON output
- ChildWaitHandle implemented with background task for waiting on kubectl child
- PortForwardSession uses Arc<TokioMutex<Option<Child>>> for async-safe child management
- Port-forward uses kubectl's dynamic port binding (0) instead of TcpListener
- Added shutdown_port_forwards command for app shutdown cleanup
- Added cleanup effect in App.tsx to call shutdownPortForwardsCmd on unmount
- Database CRUD operations for clusters and port_forwards added to db.rs
- validate_resource_name uses lazy_static! for cached Regex to prevent ReDoS
- Cluster struct updated to store kubeconfig_content directly instead of kubeconfig_id
- Cluster model in db/models.rs updated to use kubeconfig_content field
- load_clusters and load_port_forwards commands registered in lib.rs
- Temp file cleanup moved to background task in ChildWaitHandle to ensure cleanup after kubectl completes
- Unused child_id field removed from ChildWaitHandle
- Command validation moved to beginning of start_port_forward before any operations
- Fixed lint errors: removed unused imports, fixed React hooks order, updated type annotations
- Updated eslint.config.js to properly configure file patterns
2026-06-07 01:27:39 +00:00
|
|
|
clusters.remove(&id);
|
2026-06-06 17:04:21 +00:00
|
|
|
|
feat(k8s): implement clean-room Kubernetes management GUI
- Backend: kube module with ClusterClient, PortForwardSession, RefreshRegistry
- 7 Tauri IPC commands: add_cluster, remove_cluster, list_clusters, start_port_forward, stop_port_forward, list_port_forwards, delete_port_forward, shutdown_port_forwards
- AppState extended with clusters, port_forwards, refresh_registry fields
- Version bumped to 1.1.0 in Cargo.toml and package.json
- Auto-tag workflow updated to mark releases as draft (pre-release)
- Buy Me A Coffee section added to README.md
- Fixed changelog workflow to only include current tag commits
- Proper kubeconfig YAML parsing with extract_context and extract_server_url
- Added kubeconfig content storage in ClusterClient
- Updated PortForwardSession to include cluster_name
- Frontend GUI components: ClusterList, PortForwardList, AddClusterModal, PortForwardForm, KubernetesPage
- TypeScript types and IPC commands for Kubernetes management
- Unit tests for Kubernetes IPC commands (6 tests)
- All 332 Rust tests passing
- All 98 frontend tests passing
- TypeScript type checks passing
- Project builds successfully in release mode
- Committed and pushed to feature/kubernetes-management branch
- Command injection vulnerability fixed with regex validation and max length check (253 chars)
- stop_port_forward and shutdown_port_forwards properly kill kubectl child processes via async child management
- Temp file cleanup implemented with RAII TempFileCleanup struct created before std::fs::write
- discover_pods now parses actual kubectl JSON output
- ChildWaitHandle implemented with background task for waiting on kubectl child
- PortForwardSession uses Arc<TokioMutex<Option<Child>>> for async-safe child management
- Port-forward uses kubectl's dynamic port binding (0) instead of TcpListener
- Added shutdown_port_forwards command for app shutdown cleanup
- Added cleanup effect in App.tsx to call shutdownPortForwardsCmd on unmount
- Database CRUD operations for clusters and port_forwards added to db.rs
- validate_resource_name uses lazy_static! for cached Regex to prevent ReDoS
- Cluster struct updated to store kubeconfig_content directly instead of kubeconfig_id
- Cluster model in db/models.rs updated to use kubeconfig_content field
- load_clusters and load_port_forwards commands registered in lib.rs
- Temp file cleanup moved to background task in ChildWaitHandle to ensure cleanup after kubectl completes
- Unused child_id field removed from ChildWaitHandle
- Command validation moved to beginning of start_port_forward before any operations
- Fixed lint errors: removed unused imports, fixed React hooks order, updated type annotations
- Updated eslint.config.js to properly configure file patterns
2026-06-07 01:27:39 +00:00
|
|
|
// Cascade: close all port forwards for this cluster
|
2026-06-06 21:23:00 +00:00
|
|
|
let mut port_forwards = state.port_forwards.lock().await;
|
|
|
|
|
let session_ids_to_remove: Vec<String> = port_forwards
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|(_, session)| session.cluster_id == id)
|
|
|
|
|
.map(|(id, _)| id.clone())
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
for session_id in session_ids_to_remove {
|
2026-06-07 00:29:42 +00:00
|
|
|
if let Some(mut session) = port_forwards.remove(&session_id) {
|
|
|
|
|
session.close().await;
|
|
|
|
|
}
|
2026-06-06 21:23:00 +00:00
|
|
|
}
|
|
|
|
|
|
2026-06-06 16:41:23 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-06 17:04:21 +00:00
|
|
|
pub async fn list_clusters(state: State<'_, AppState>) -> Result<Vec<ClusterInfo>, String> {
|
2026-06-06 16:41:23 +00:00
|
|
|
let clusters = state.clusters.lock().await;
|
2026-06-06 17:04:21 +00:00
|
|
|
|
2026-06-06 16:41:23 +00:00
|
|
|
let cluster_list: Vec<ClusterInfo> = clusters
|
|
|
|
|
.values()
|
|
|
|
|
.map(|c| ClusterInfo {
|
|
|
|
|
id: c.id.clone(),
|
|
|
|
|
name: c.name.clone(),
|
|
|
|
|
context: c.context.clone(),
|
|
|
|
|
cluster_url: c.server_url.clone(),
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
Ok(cluster_list)
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 20:14:04 +00:00
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn test_cluster_connection(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<ClusterConnectionStatus, 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;
|
|
|
|
|
|
2026-06-06 23:40:52 +00:00
|
|
|
// Write kubeconfig to temp file and ensure cleanup even on panic
|
2026-06-06 20:14:04 +00:00
|
|
|
let temp_dir = std::env::temp_dir();
|
|
|
|
|
let temp_path = temp_dir.join(format!("kubeconfig-{}.yaml", cluster_id));
|
2026-06-06 23:32:08 +00:00
|
|
|
let _cleanup = TempFileCleanup(temp_path.clone());
|
|
|
|
|
|
2026-06-06 23:40:52 +00:00
|
|
|
std::fs::write(&temp_path, kubeconfig_content)
|
|
|
|
|
.map_err(|e| format!("Failed to write kubeconfig temp file: {e}"))?;
|
|
|
|
|
|
2026-06-06 20:14:04 +00:00
|
|
|
// Run kubectl cluster-info
|
|
|
|
|
let kubectl_path = locate_kubectl()?;
|
|
|
|
|
|
|
|
|
|
let output = Command::new(kubectl_path)
|
|
|
|
|
.arg("cluster-info")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
2026-06-06 20:14:04 +00:00
|
|
|
.output()
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|e| format!("Failed to execute kubectl: {e}"))?;
|
|
|
|
|
|
|
|
|
|
let status = if output.status.success() {
|
|
|
|
|
ClusterConnectionState::Connected
|
|
|
|
|
} else {
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
|
|
|
ClusterConnectionState::Disconnected {
|
|
|
|
|
error: stderr.to_string(),
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Ok(ClusterConnectionStatus {
|
|
|
|
|
status,
|
|
|
|
|
context: context.clone(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn discover_pods(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<PodInfo>, 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;
|
|
|
|
|
|
2026-06-06 23:40:52 +00:00
|
|
|
// Write kubeconfig to temp file and ensure cleanup even on panic
|
2026-06-06 20:14:04 +00:00
|
|
|
let temp_dir = std::env::temp_dir();
|
|
|
|
|
let temp_path = temp_dir.join(format!("kubeconfig-{}-pods.yaml", cluster_id));
|
2026-06-06 21:54:30 +00:00
|
|
|
let _cleanup = TempFileCleanup(temp_path.clone());
|
|
|
|
|
|
2026-06-06 23:40:52 +00:00
|
|
|
std::fs::write(&temp_path, kubeconfig_content)
|
|
|
|
|
.map_err(|e| format!("Failed to write kubeconfig temp file: {e}"))?;
|
|
|
|
|
|
2026-06-06 21:54:30 +00:00
|
|
|
// Run kubectl get pods with full JSON output
|
2026-06-06 20:14:04 +00:00
|
|
|
let kubectl_path = locate_kubectl()?;
|
|
|
|
|
|
|
|
|
|
let output = Command::new(kubectl_path)
|
|
|
|
|
.arg("get")
|
|
|
|
|
.arg("pods")
|
|
|
|
|
.arg("-n")
|
|
|
|
|
.arg(&namespace)
|
|
|
|
|
.arg("-o")
|
2026-06-06 21:54:30 +00:00
|
|
|
.arg("json")
|
2026-06-06 20:14:04 +00:00
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
2026-06-06 20:14:04 +00:00
|
|
|
.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(format!("Failed to list pods: {}", stderr));
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 21:54:30 +00:00
|
|
|
// Parse actual JSON output to get real pod information
|
2026-06-06 20:14:04 +00:00
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
2026-06-06 21:54:30 +00:00
|
|
|
let pods = parse_pods_json(&stdout)?;
|
2026-06-06 20:14:04 +00:00
|
|
|
|
|
|
|
|
Ok(pods)
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 21:23:00 +00:00
|
|
|
// Regex patterns for Kubernetes resource names
|
|
|
|
|
// Must match: ^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$ (DNS subdomain name)
|
2026-06-06 21:54:30 +00:00
|
|
|
// Added max length check (253 chars) to prevent ReDoS attacks
|
|
|
|
|
const MAX_NAME_LENGTH: usize = 253;
|
|
|
|
|
|
|
|
|
|
/// Validates a Kubernetes resource name against DNS subdomain naming rules.
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
/// * `name` - The name to validate
|
|
|
|
|
/// * `field_name` - The field name for error messages
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
/// * `Ok(())` if the name is valid
|
|
|
|
|
/// * `Err(String)` with an error message if the name is invalid
|
|
|
|
|
pub fn validate_resource_name(name: &str, field_name: &str) -> Result<(), String> {
|
|
|
|
|
// Check max length to prevent ReDoS attacks
|
|
|
|
|
if name.len() > MAX_NAME_LENGTH {
|
|
|
|
|
return Err(format!(
|
|
|
|
|
"{} '{}' exceeds maximum length of {} characters",
|
|
|
|
|
field_name, name, MAX_NAME_LENGTH
|
|
|
|
|
));
|
|
|
|
|
}
|
2026-06-06 21:23:00 +00:00
|
|
|
|
2026-06-06 21:54:30 +00:00
|
|
|
// Reject names starting with hyphens or dots
|
|
|
|
|
if name.starts_with('-') || name.starts_with('.') {
|
|
|
|
|
return Err(format!(
|
|
|
|
|
"{} '{}' cannot start with a hyphen or dot",
|
|
|
|
|
field_name, name
|
|
|
|
|
));
|
|
|
|
|
}
|
2026-06-06 21:23:00 +00:00
|
|
|
|
2026-06-06 21:54:30 +00:00
|
|
|
// Reject names ending with hyphens or dots
|
|
|
|
|
if name.ends_with('-') || name.ends_with('.') {
|
2026-06-06 21:23:00 +00:00
|
|
|
return Err(format!(
|
2026-06-06 21:54:30 +00:00
|
|
|
"{} '{}' cannot end with a hyphen or dot",
|
|
|
|
|
field_name, name
|
2026-06-06 21:23:00 +00:00
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 23:32:08 +00:00
|
|
|
// Use cached regex pattern
|
|
|
|
|
if !NAME_PATTERN_REGEX.is_match(name) {
|
2026-06-06 21:23:00 +00:00
|
|
|
return Err(format!(
|
2026-06-06 21:54:30 +00:00
|
|
|
"{} '{}' does not match pattern {}",
|
2026-06-06 23:32:08 +00:00
|
|
|
field_name, name, r"^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$"
|
2026-06-06 21:23:00 +00:00
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 21:54:30 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn start_port_forward(
|
|
|
|
|
request: PortForwardRequest,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<PortForwardResponse, String> {
|
|
|
|
|
let session_id = uuid::Uuid::now_v7().to_string();
|
|
|
|
|
|
2026-06-07 00:44:32 +00:00
|
|
|
// Validate namespace and pod names FIRST to prevent command injection
|
|
|
|
|
// Validation must happen before any operations to prevent partial state creation
|
2026-06-06 21:54:30 +00:00
|
|
|
validate_resource_name(&request.namespace, "namespace")?;
|
|
|
|
|
validate_resource_name(&request.pod, "pod")?;
|
|
|
|
|
|
2026-06-06 18:28:03 +00:00
|
|
|
let clusters = state.clusters.lock().await;
|
2026-06-06 18:30:35 +00:00
|
|
|
let cluster = clusters
|
|
|
|
|
.get(&request.cluster_id)
|
2026-06-06 18:28:03 +00:00
|
|
|
.ok_or_else(|| format!("Cluster {} not found", request.cluster_id))?;
|
|
|
|
|
|
|
|
|
|
let cluster_name = cluster.name.clone();
|
2026-06-06 20:14:04 +00:00
|
|
|
let kubeconfig_content = cluster.kubeconfig_content.clone();
|
|
|
|
|
|
2026-06-06 21:23:00 +00:00
|
|
|
// Use kubectl's dynamic port binding by specifying 0 as local port
|
|
|
|
|
// This avoids race condition with port allocation
|
2026-06-07 00:46:04 +00:00
|
|
|
// Note: Dynamic port allocation (when local_port=0) currently returns 0
|
|
|
|
|
// The actual allocated port could be captured from kubectl's stderr/stdout
|
|
|
|
|
// but this requires parsing kubectl output which is complex and error-prone
|
|
|
|
|
// For now, users must specify a local port or use the default behavior
|
2026-06-06 21:23:00 +00:00
|
|
|
let local_port = if request.local_port > 0 {
|
|
|
|
|
request.local_port
|
|
|
|
|
} else {
|
2026-06-07 00:46:04 +00:00
|
|
|
0 // Let kubectl allocate dynamically (currently not captured)
|
2026-06-06 21:23:00 +00:00
|
|
|
};
|
2026-06-06 20:14:04 +00:00
|
|
|
|
2026-06-06 21:23:00 +00:00
|
|
|
info!(
|
2026-06-06 20:14:04 +00:00
|
|
|
session_id = %session_id,
|
|
|
|
|
cluster_id = %request.cluster_id,
|
|
|
|
|
namespace = %request.namespace,
|
|
|
|
|
pod = %request.pod,
|
|
|
|
|
container_port = request.container_port,
|
|
|
|
|
local_port,
|
|
|
|
|
"Allocating local port for port-forward"
|
|
|
|
|
);
|
|
|
|
|
|
feat(k8s): implement clean-room Kubernetes management GUI
- Backend: kube module with ClusterClient, PortForwardSession, RefreshRegistry
- 7 Tauri IPC commands: add_cluster, remove_cluster, list_clusters, start_port_forward, stop_port_forward, list_port_forwards, delete_port_forward, shutdown_port_forwards
- AppState extended with clusters, port_forwards, refresh_registry fields
- Version bumped to 1.1.0 in Cargo.toml and package.json
- Auto-tag workflow updated to mark releases as draft (pre-release)
- Buy Me A Coffee section added to README.md
- Fixed changelog workflow to only include current tag commits
- Proper kubeconfig YAML parsing with extract_context and extract_server_url
- Added kubeconfig content storage in ClusterClient
- Updated PortForwardSession to include cluster_name
- Frontend GUI components: ClusterList, PortForwardList, AddClusterModal, PortForwardForm, KubernetesPage
- TypeScript types and IPC commands for Kubernetes management
- Unit tests for Kubernetes IPC commands (6 tests)
- All 332 Rust tests passing
- All 98 frontend tests passing
- TypeScript type checks passing
- Project builds successfully in release mode
- Committed and pushed to feature/kubernetes-management branch
- Command injection vulnerability fixed with regex validation and max length check (253 chars)
- stop_port_forward and shutdown_port_forwards properly kill kubectl child processes via async child management
- Temp file cleanup implemented with RAII TempFileCleanup struct created before std::fs::write
- discover_pods now parses actual kubectl JSON output
- ChildWaitHandle implemented with background task for waiting on kubectl child
- PortForwardSession uses Arc<TokioMutex<Option<Child>>> for async-safe child management
- Port-forward uses kubectl's dynamic port binding (0) instead of TcpListener
- Added shutdown_port_forwards command for app shutdown cleanup
- Added cleanup effect in App.tsx to call shutdownPortForwardsCmd on unmount
- Database CRUD operations for clusters and port_forwards added to db.rs
- validate_resource_name uses lazy_static! for cached Regex to prevent ReDoS
- Cluster struct updated to store kubeconfig_content directly instead of kubeconfig_id
- Cluster model in db/models.rs updated to use kubeconfig_content field
- load_clusters and load_port_forwards commands registered in lib.rs
- Temp file cleanup moved to background task in ChildWaitHandle to ensure cleanup after kubectl completes
- Unused child_id field removed from ChildWaitHandle
- Command validation moved to beginning of start_port_forward before any operations
- Fixed lint errors: removed unused imports, fixed React hooks order, updated type annotations
- Updated eslint.config.js to properly configure file patterns
2026-06-07 01:27:39 +00:00
|
|
|
// Write kubeconfig to temp file
|
2026-06-06 20:14:04 +00:00
|
|
|
let temp_dir = std::env::temp_dir();
|
|
|
|
|
let temp_path = temp_dir.join(format!("kubeconfig-{}.yaml", request.cluster_id));
|
2026-06-07 00:29:42 +00:00
|
|
|
|
2026-06-06 23:40:52 +00:00
|
|
|
std::fs::write(&temp_path, kubeconfig_content.as_ref())
|
|
|
|
|
.map_err(|e| format!("Failed to write kubeconfig temp file: {e}"))?;
|
|
|
|
|
|
2026-06-06 20:14:04 +00:00
|
|
|
// Build kubectl command
|
|
|
|
|
let kubectl_path = locate_kubectl()?;
|
|
|
|
|
let args = vec![
|
|
|
|
|
"port-forward".to_string(),
|
|
|
|
|
format!("pod/{}", request.pod),
|
|
|
|
|
format!("{}:{}", local_port, request.container_port),
|
|
|
|
|
"-n".to_string(),
|
|
|
|
|
request.namespace.clone(),
|
|
|
|
|
];
|
|
|
|
|
|
2026-06-06 21:23:00 +00:00
|
|
|
info!(
|
2026-06-06 20:14:04 +00:00
|
|
|
session_id = %session_id,
|
|
|
|
|
command = ?args,
|
|
|
|
|
"Spawning kubectl port-forward subprocess"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Spawn kubectl subprocess
|
|
|
|
|
let child = Command::new(kubectl_path)
|
|
|
|
|
.args(&args)
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(cluster.context.as_str())
|
2026-06-06 20:14:04 +00:00
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
|
|
|
|
.spawn()
|
|
|
|
|
.map_err(|e| format!("Failed to spawn kubectl: {e}"))?;
|
2026-06-06 18:28:03 +00:00
|
|
|
|
2026-06-06 20:14:04 +00:00
|
|
|
// Create session with allocated port
|
2026-06-06 23:32:08 +00:00
|
|
|
let session = PortForwardSession::new(PortForwardSessionConfig {
|
2026-06-06 18:47:51 +00:00
|
|
|
id: session_id.clone(),
|
|
|
|
|
cluster_id: request.cluster_id.clone(),
|
2026-06-06 18:28:03 +00:00
|
|
|
cluster_name,
|
2026-06-06 18:47:51 +00:00
|
|
|
namespace: request.namespace.clone(),
|
|
|
|
|
pod: request.pod.clone(),
|
|
|
|
|
container: None,
|
|
|
|
|
ports: vec![request.container_port],
|
2026-06-06 20:14:04 +00:00
|
|
|
local_ports: vec![local_port],
|
2026-06-06 23:32:08 +00:00
|
|
|
temp_kubeconfig_path: Some(temp_path),
|
2026-06-06 18:47:51 +00:00
|
|
|
});
|
2026-06-06 16:41:23 +00:00
|
|
|
|
2026-06-06 23:32:08 +00:00
|
|
|
// Store child handle in session - spawn background task to wait on child
|
2026-06-06 16:41:23 +00:00
|
|
|
{
|
|
|
|
|
let mut port_forwards = state.port_forwards.lock().await;
|
2026-06-06 21:23:00 +00:00
|
|
|
port_forwards.insert(session_id.clone(), session);
|
2026-06-06 20:14:04 +00:00
|
|
|
let session_mut = port_forwards.get_mut(&session_id).unwrap();
|
2026-06-06 23:32:08 +00:00
|
|
|
session_mut.spawn_child_waiter(child);
|
2026-06-06 16:41:23 +00:00
|
|
|
}
|
|
|
|
|
|
2026-06-06 21:23:00 +00:00
|
|
|
info!(
|
2026-06-06 20:14:04 +00:00
|
|
|
session_id = %session_id,
|
|
|
|
|
local_port,
|
|
|
|
|
"Port-forward session started"
|
|
|
|
|
);
|
|
|
|
|
|
2026-06-06 16:41:23 +00:00
|
|
|
Ok(PortForwardResponse {
|
|
|
|
|
id: session_id,
|
|
|
|
|
cluster_id: request.cluster_id,
|
|
|
|
|
namespace: request.namespace,
|
|
|
|
|
pod: request.pod,
|
2026-06-06 21:23:00 +00:00
|
|
|
container_ports: vec![request.container_port],
|
|
|
|
|
local_ports: vec![local_port],
|
2026-06-06 16:41:23 +00:00
|
|
|
status: "Active".to_string(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-06 17:04:21 +00:00
|
|
|
pub async fn stop_port_forward(id: String, state: State<'_, AppState>) -> Result<(), String> {
|
2026-06-06 16:41:23 +00:00
|
|
|
let mut port_forwards = state.port_forwards.lock().await;
|
2026-06-06 17:04:21 +00:00
|
|
|
|
2026-06-06 16:41:23 +00:00
|
|
|
if let Some(session) = port_forwards.get_mut(&id) {
|
2026-06-06 23:32:08 +00:00
|
|
|
session.stop_async().await;
|
2026-06-06 21:23:00 +00:00
|
|
|
info!(session_id = %id, "Port-forward session stopped");
|
2026-06-06 16:41:23 +00:00
|
|
|
Ok(())
|
|
|
|
|
} else {
|
|
|
|
|
Err(format!("Port forward session {id} not found"))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn list_port_forwards(
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<PortForwardResponse>, String> {
|
|
|
|
|
let port_forwards = state.port_forwards.lock().await;
|
2026-06-06 17:04:21 +00:00
|
|
|
|
feat(k8s): implement clean-room Kubernetes management GUI
- Backend: kube module with ClusterClient, PortForwardSession, RefreshRegistry
- 7 Tauri IPC commands: add_cluster, remove_cluster, list_clusters, start_port_forward, stop_port_forward, list_port_forwards, delete_port_forward, shutdown_port_forwards
- AppState extended with clusters, port_forwards, refresh_registry fields
- Version bumped to 1.1.0 in Cargo.toml and package.json
- Auto-tag workflow updated to mark releases as draft (pre-release)
- Buy Me A Coffee section added to README.md
- Fixed changelog workflow to only include current tag commits
- Proper kubeconfig YAML parsing with extract_context and extract_server_url
- Added kubeconfig content storage in ClusterClient
- Updated PortForwardSession to include cluster_name
- Frontend GUI components: ClusterList, PortForwardList, AddClusterModal, PortForwardForm, KubernetesPage
- TypeScript types and IPC commands for Kubernetes management
- Unit tests for Kubernetes IPC commands (6 tests)
- All 332 Rust tests passing
- All 98 frontend tests passing
- TypeScript type checks passing
- Project builds successfully in release mode
- Committed and pushed to feature/kubernetes-management branch
- Command injection vulnerability fixed with regex validation and max length check (253 chars)
- stop_port_forward and shutdown_port_forwards properly kill kubectl child processes via async child management
- Temp file cleanup implemented with RAII TempFileCleanup struct created before std::fs::write
- discover_pods now parses actual kubectl JSON output
- ChildWaitHandle implemented with background task for waiting on kubectl child
- PortForwardSession uses Arc<TokioMutex<Option<Child>>> for async-safe child management
- Port-forward uses kubectl's dynamic port binding (0) instead of TcpListener
- Added shutdown_port_forwards command for app shutdown cleanup
- Added cleanup effect in App.tsx to call shutdownPortForwardsCmd on unmount
- Database CRUD operations for clusters and port_forwards added to db.rs
- validate_resource_name uses lazy_static! for cached Regex to prevent ReDoS
- Cluster struct updated to store kubeconfig_content directly instead of kubeconfig_id
- Cluster model in db/models.rs updated to use kubeconfig_content field
- load_clusters and load_port_forwards commands registered in lib.rs
- Temp file cleanup moved to background task in ChildWaitHandle to ensure cleanup after kubectl completes
- Unused child_id field removed from ChildWaitHandle
- Command validation moved to beginning of start_port_forward before any operations
- Fixed lint errors: removed unused imports, fixed React hooks order, updated type annotations
- Updated eslint.config.js to properly configure file patterns
2026-06-07 01:27:39 +00:00
|
|
|
let mut forwards = Vec::new();
|
|
|
|
|
for s in port_forwards.values() {
|
|
|
|
|
let status_str = {
|
|
|
|
|
let status = s.shared_status.lock().await;
|
|
|
|
|
match &*status {
|
|
|
|
|
crate::kube::PortForwardStatus::Active => "Active".to_string(),
|
|
|
|
|
crate::kube::PortForwardStatus::Stopped => "Stopped".to_string(),
|
|
|
|
|
crate::kube::PortForwardStatus::Error(e) => e.clone(),
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
forwards.push(PortForwardResponse {
|
2026-06-06 16:41:23 +00:00
|
|
|
id: s.id.clone(),
|
|
|
|
|
cluster_id: s.cluster_id.clone(),
|
|
|
|
|
namespace: s.namespace.clone(),
|
|
|
|
|
pod: s.pod.clone(),
|
2026-06-06 21:23:00 +00:00
|
|
|
container_ports: s.ports.clone(),
|
|
|
|
|
local_ports: s.local_ports.clone(),
|
feat(k8s): implement clean-room Kubernetes management GUI
- Backend: kube module with ClusterClient, PortForwardSession, RefreshRegistry
- 7 Tauri IPC commands: add_cluster, remove_cluster, list_clusters, start_port_forward, stop_port_forward, list_port_forwards, delete_port_forward, shutdown_port_forwards
- AppState extended with clusters, port_forwards, refresh_registry fields
- Version bumped to 1.1.0 in Cargo.toml and package.json
- Auto-tag workflow updated to mark releases as draft (pre-release)
- Buy Me A Coffee section added to README.md
- Fixed changelog workflow to only include current tag commits
- Proper kubeconfig YAML parsing with extract_context and extract_server_url
- Added kubeconfig content storage in ClusterClient
- Updated PortForwardSession to include cluster_name
- Frontend GUI components: ClusterList, PortForwardList, AddClusterModal, PortForwardForm, KubernetesPage
- TypeScript types and IPC commands for Kubernetes management
- Unit tests for Kubernetes IPC commands (6 tests)
- All 332 Rust tests passing
- All 98 frontend tests passing
- TypeScript type checks passing
- Project builds successfully in release mode
- Committed and pushed to feature/kubernetes-management branch
- Command injection vulnerability fixed with regex validation and max length check (253 chars)
- stop_port_forward and shutdown_port_forwards properly kill kubectl child processes via async child management
- Temp file cleanup implemented with RAII TempFileCleanup struct created before std::fs::write
- discover_pods now parses actual kubectl JSON output
- ChildWaitHandle implemented with background task for waiting on kubectl child
- PortForwardSession uses Arc<TokioMutex<Option<Child>>> for async-safe child management
- Port-forward uses kubectl's dynamic port binding (0) instead of TcpListener
- Added shutdown_port_forwards command for app shutdown cleanup
- Added cleanup effect in App.tsx to call shutdownPortForwardsCmd on unmount
- Database CRUD operations for clusters and port_forwards added to db.rs
- validate_resource_name uses lazy_static! for cached Regex to prevent ReDoS
- Cluster struct updated to store kubeconfig_content directly instead of kubeconfig_id
- Cluster model in db/models.rs updated to use kubeconfig_content field
- load_clusters and load_port_forwards commands registered in lib.rs
- Temp file cleanup moved to background task in ChildWaitHandle to ensure cleanup after kubectl completes
- Unused child_id field removed from ChildWaitHandle
- Command validation moved to beginning of start_port_forward before any operations
- Fixed lint errors: removed unused imports, fixed React hooks order, updated type annotations
- Updated eslint.config.js to properly configure file patterns
2026-06-07 01:27:39 +00:00
|
|
|
status: status_str,
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-06-06 16:41:23 +00:00
|
|
|
|
|
|
|
|
Ok(forwards)
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 18:09:14 +00:00
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn delete_port_forward(id: String, state: State<'_, AppState>) -> Result<(), String> {
|
2026-06-07 00:29:42 +00:00
|
|
|
// Delete from database
|
|
|
|
|
{
|
|
|
|
|
let db = state.db.lock().map_err(|e| e.to_string())?;
|
|
|
|
|
db.execute("DELETE FROM port_forwards WHERE id = ?1", [&id])
|
|
|
|
|
.map_err(|e| format!("Failed to delete port forward: {e}"))?;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-06 18:09:14 +00:00
|
|
|
let mut port_forwards = state.port_forwards.lock().await;
|
|
|
|
|
|
2026-06-06 23:40:52 +00:00
|
|
|
if let Some(mut session) = port_forwards.remove(&id) {
|
|
|
|
|
// Close the session to kill the child and clean up temp files
|
|
|
|
|
session.close().await;
|
|
|
|
|
} else {
|
2026-06-06 18:09:14 +00:00
|
|
|
return Err(format!("Port forward session {id} not found"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2026-06-06 20:14:04 +00:00
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_cluster_info_serialization() {
|
|
|
|
|
let info = ClusterInfo {
|
|
|
|
|
id: "cluster-1".to_string(),
|
|
|
|
|
name: "Production".to_string(),
|
|
|
|
|
context: "prod-context".to_string(),
|
|
|
|
|
cluster_url: "https://k8s.example.com".to_string(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let json = serde_json::to_string(&info).unwrap();
|
|
|
|
|
let parsed: ClusterInfo = serde_json::from_str(&json).unwrap();
|
|
|
|
|
|
|
|
|
|
assert_eq!(info.id, parsed.id);
|
|
|
|
|
assert_eq!(info.name, parsed.name);
|
|
|
|
|
assert_eq!(info.context, parsed.context);
|
|
|
|
|
assert_eq!(info.cluster_url, parsed.cluster_url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_cluster_connection_state_serialization() {
|
|
|
|
|
let connected = ClusterConnectionState::Connected;
|
|
|
|
|
let json = serde_json::to_string(&connected).unwrap();
|
|
|
|
|
let parsed: ClusterConnectionState = serde_json::from_str(&json).unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(matches!(parsed, ClusterConnectionState::Connected));
|
|
|
|
|
|
|
|
|
|
let disconnected = ClusterConnectionState::Disconnected {
|
|
|
|
|
error: "connection refused".to_string(),
|
|
|
|
|
};
|
|
|
|
|
let json = serde_json::to_string(&disconnected).unwrap();
|
|
|
|
|
let parsed: ClusterConnectionState = serde_json::from_str(&json).unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(matches!(
|
|
|
|
|
parsed,
|
|
|
|
|
ClusterConnectionState::Disconnected { .. }
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_port_forward_request_serialization() {
|
|
|
|
|
let request = PortForwardRequest {
|
|
|
|
|
cluster_id: "cluster-1".to_string(),
|
|
|
|
|
namespace: "default".to_string(),
|
|
|
|
|
pod: "my-pod-abc123".to_string(),
|
|
|
|
|
container_port: 8080,
|
2026-06-06 21:23:00 +00:00
|
|
|
local_port: 0,
|
2026-06-06 20:14:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let json = serde_json::to_string(&request).unwrap();
|
|
|
|
|
let parsed: PortForwardRequest = serde_json::from_str(&json).unwrap();
|
|
|
|
|
|
|
|
|
|
assert_eq!(request.cluster_id, parsed.cluster_id);
|
|
|
|
|
assert_eq!(request.namespace, parsed.namespace);
|
|
|
|
|
assert_eq!(request.pod, parsed.pod);
|
|
|
|
|
assert_eq!(request.container_port, parsed.container_port);
|
2026-06-06 21:23:00 +00:00
|
|
|
assert_eq!(request.local_port, parsed.local_port);
|
2026-06-06 20:14:04 +00:00
|
|
|
}
|
2026-06-06 23:32:08 +00:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_validate_resource_name_valid() {
|
|
|
|
|
// Valid names
|
|
|
|
|
assert!(validate_resource_name("my-pod", "pod").is_ok());
|
|
|
|
|
assert!(validate_resource_name("my-pod-123", "pod").is_ok());
|
|
|
|
|
assert!(validate_resource_name("a", "pod").is_ok());
|
|
|
|
|
assert!(validate_resource_name("my.pod.name", "pod").is_ok());
|
|
|
|
|
assert!(validate_resource_name("123", "pod").is_ok());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_validate_resource_name_invalid() {
|
|
|
|
|
// Invalid names
|
|
|
|
|
assert!(validate_resource_name("-mypod", "pod").is_err());
|
|
|
|
|
assert!(validate_resource_name("mypod-", "pod").is_err());
|
|
|
|
|
assert!(validate_resource_name(".mypod", "pod").is_err());
|
|
|
|
|
assert!(validate_resource_name("mypod.", "pod").is_err());
|
|
|
|
|
assert!(validate_resource_name("MYPOD", "pod").is_err());
|
|
|
|
|
assert!(validate_resource_name("my_pod", "pod").is_err());
|
|
|
|
|
assert!(validate_resource_name("", "pod").is_err());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_validate_resource_name_length() {
|
|
|
|
|
// Too long names
|
|
|
|
|
let long_name = "a".repeat(254);
|
|
|
|
|
assert!(validate_resource_name(&long_name, "pod").is_err());
|
|
|
|
|
}
|
2026-06-06 20:14:04 +00:00
|
|
|
}
|
2026-06-06 23:46:13 +00:00
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn shutdown_port_forwards(state: State<'_, AppState>) -> Result<(), String> {
|
|
|
|
|
let mut port_forwards = state.port_forwards.lock().await;
|
2026-06-07 00:29:42 +00:00
|
|
|
|
2026-06-06 23:46:13 +00:00
|
|
|
// Close all active port forward sessions
|
|
|
|
|
let session_ids: Vec<String> = port_forwards.keys().cloned().collect();
|
2026-06-07 00:29:42 +00:00
|
|
|
|
2026-06-06 23:46:13 +00:00
|
|
|
for session_id in session_ids {
|
|
|
|
|
if let Some(mut session) = port_forwards.remove(&session_id) {
|
|
|
|
|
session.close().await;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-06-07 00:29:42 +00:00
|
|
|
|
2026-06-06 23:46:13 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
// New Resource Discovery Commands (Phase 1)
|
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct NamespaceInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub status: String,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ServicePort {
|
|
|
|
|
pub name: Option<String>,
|
|
|
|
|
pub port: u16,
|
|
|
|
|
pub target_port: Option<String>,
|
|
|
|
|
pub protocol: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ServiceInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
|
pub service_type: String,
|
|
|
|
|
pub cluster_ip: String,
|
|
|
|
|
pub external_ip: Option<String>,
|
|
|
|
|
pub ports: Vec<ServicePort>,
|
|
|
|
|
pub age: String,
|
|
|
|
|
pub selector: std::collections::HashMap<String, String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct DeploymentInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub ready: String,
|
|
|
|
|
pub up_to_date: String,
|
|
|
|
|
pub available: String,
|
|
|
|
|
pub age: String,
|
|
|
|
|
pub replicas: i32,
|
|
|
|
|
pub labels: std::collections::HashMap<String, String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct StatefulSetInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub ready: String,
|
|
|
|
|
pub age: String,
|
|
|
|
|
pub replicas: i32,
|
|
|
|
|
pub labels: std::collections::HashMap<String, String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct DaemonSetInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub desired: i32,
|
|
|
|
|
pub current: i32,
|
|
|
|
|
pub ready: i32,
|
|
|
|
|
pub up_to_date: i32,
|
|
|
|
|
pub available: i32,
|
|
|
|
|
pub age: String,
|
|
|
|
|
pub labels: std::collections::HashMap<String, String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_namespaces(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<NamespaceInfo>, String> {
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
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-{}-namespaces.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("get")
|
|
|
|
|
.arg("namespaces")
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_namespaces_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_namespaces_json(json_str: &str) -> Result<Vec<NamespaceInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let value: Value = serde_json::from_str(json_str)
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.and_then(|i| i.as_array())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut namespaces = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let status = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("phase"))
|
|
|
|
|
.and_then(|p| p.as_str())
|
|
|
|
|
.unwrap_or("Unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
2026-06-07 05:25:42 +00:00
|
|
|
namespaces.push(NamespaceInfo { name, status, age });
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(namespaces)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_pods(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<PodInfo>, String> {
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
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-{}-pods.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()?;
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let mut kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("pods");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_pods_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_pods_json(json_str: &str) -> Result<Vec<PodInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let value: Value = serde_json::from_str(json_str)
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.and_then(|i| i.as_array())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut pods = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let status = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("phase"))
|
|
|
|
|
.and_then(|p| p.as_str())
|
|
|
|
|
.unwrap_or("Unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let ready = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("containerStatuses"))
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.and_then(|c| c.as_array())
|
|
|
|
|
.map(|container_statuses| {
|
2026-06-07 05:25:42 +00:00
|
|
|
let ready_count = container_statuses
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|c| c.get("ready").and_then(|r| r.as_bool()).unwrap_or(false))
|
|
|
|
|
.count();
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let total_count = container_statuses.len();
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
format!("{}/{}", ready_count, total_count)
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or("0/0".to_string());
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let containers = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("containers"))
|
|
|
|
|
.and_then(|c| c.as_array())
|
|
|
|
|
.map(|spec_containers| {
|
|
|
|
|
spec_containers
|
|
|
|
|
.iter()
|
2026-06-07 05:25:42 +00:00
|
|
|
.filter_map(|c| {
|
|
|
|
|
c.get("name")
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.map(|s| s.to_string())
|
|
|
|
|
})
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.collect()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
pods.push(PodInfo {
|
|
|
|
|
name,
|
|
|
|
|
status,
|
|
|
|
|
ready,
|
|
|
|
|
age,
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
containers,
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(pods)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_services(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<ServiceInfo>, String> {
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
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-{}-services.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()?;
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let mut kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("services");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_services_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_services_json(json_str: &str) -> Result<Vec<ServiceInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let value: Value = serde_json::from_str(json_str)
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.and_then(|i| i.as_array())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut services = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let service_type = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("type"))
|
|
|
|
|
.and_then(|t| t.as_str())
|
|
|
|
|
.unwrap_or("ClusterIP")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let cluster_ip = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("clusterIP"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.unwrap_or("None")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let external_ip = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("loadBalancer"))
|
|
|
|
|
.and_then(|l| l.get("ingress"))
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.and_then(|i| i.as_array())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.and_then(|ingress| ingress.first())
|
|
|
|
|
.and_then(|ing| ing.get("ip"))
|
|
|
|
|
.and_then(|ip| ip.as_str())
|
|
|
|
|
.map(|s| s.to_string());
|
|
|
|
|
|
|
|
|
|
let ports = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("ports"))
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.and_then(|p| p.as_array())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.map(|ports_seq| {
|
2026-06-07 05:25:42 +00:00
|
|
|
ports_seq
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|p| ServicePort {
|
|
|
|
|
name: p
|
|
|
|
|
.get("name")
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.map(|s| s.to_string()),
|
|
|
|
|
port: p.get("port").and_then(|p| p.as_u64()).unwrap_or(0) as u16,
|
|
|
|
|
target_port: p
|
|
|
|
|
.get("targetPort")
|
|
|
|
|
.and_then(|tp| tp.as_str())
|
|
|
|
|
.map(|s| s.to_string()),
|
|
|
|
|
protocol: p
|
|
|
|
|
.get("protocol")
|
|
|
|
|
.and_then(|p| p.as_str())
|
|
|
|
|
.unwrap_or("TCP")
|
|
|
|
|
.to_string(),
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
})
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let selector = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("selector"))
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.and_then(|s| s.as_object())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.map(|s| {
|
2026-06-07 05:25:42 +00:00
|
|
|
s.iter()
|
|
|
|
|
.map(|(k, v)| (k.clone(), v.as_str().unwrap_or("").to_string()))
|
|
|
|
|
.collect()
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
})
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
services.push(ServiceInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
service_type,
|
|
|
|
|
cluster_ip,
|
|
|
|
|
external_ip,
|
|
|
|
|
ports,
|
|
|
|
|
age,
|
|
|
|
|
selector,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(services)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_deployments(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<DeploymentInfo>, String> {
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
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-{}-deployments.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()?;
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let mut kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("deployments");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_deployments_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_deployments_json(json_str: &str) -> Result<Vec<DeploymentInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let value: Value = serde_json::from_str(json_str)
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.and_then(|i| i.as_array())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut deployments = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let replicas = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("replicas"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.unwrap_or(0) as i32;
|
|
|
|
|
|
|
|
|
|
let ready = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("readyReplicas"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.map(|r| format!("{}/{}", r, replicas))
|
|
|
|
|
.unwrap_or_else(|| format!("0/{}", replicas));
|
|
|
|
|
|
|
|
|
|
let up_to_date = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("updatedReplicas"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.map(|r| r.to_string())
|
|
|
|
|
.unwrap_or_else(|| "N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let available = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("availableReplicas"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.map(|r| r.to_string())
|
|
|
|
|
.unwrap_or_else(|| "N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let labels = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("labels"))
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.and_then(|l| l.as_object())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.map(|l| {
|
2026-06-07 05:25:42 +00:00
|
|
|
l.iter()
|
|
|
|
|
.map(|(k, v)| (k.clone(), v.as_str().unwrap_or("").to_string()))
|
|
|
|
|
.collect()
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
})
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
deployments.push(DeploymentInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
ready,
|
|
|
|
|
up_to_date,
|
|
|
|
|
available,
|
|
|
|
|
age,
|
|
|
|
|
replicas,
|
|
|
|
|
labels,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(deployments)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_statefulsets(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<StatefulSetInfo>, String> {
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
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-{}-statefulsets.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()?;
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let mut kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("statefulsets");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_statefulsets_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_statefulsets_json(json_str: &str) -> Result<Vec<StatefulSetInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let value: Value = serde_json::from_str(json_str)
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.and_then(|i| i.as_array())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut statefulsets = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let replicas = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("replicas"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.unwrap_or(0) as i32;
|
|
|
|
|
|
|
|
|
|
let ready = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("readyReplicas"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.map(|r| format!("{}/{}", r, replicas))
|
|
|
|
|
.unwrap_or_else(|| format!("0/{}", replicas));
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let labels = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("labels"))
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.and_then(|l| l.as_object())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.map(|l| {
|
2026-06-07 05:25:42 +00:00
|
|
|
l.iter()
|
|
|
|
|
.map(|(k, v)| (k.clone(), v.as_str().unwrap_or("").to_string()))
|
|
|
|
|
.collect()
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
})
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
statefulsets.push(StatefulSetInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
ready,
|
|
|
|
|
age,
|
|
|
|
|
replicas,
|
|
|
|
|
labels,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(statefulsets)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_daemonsets(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<DaemonSetInfo>, String> {
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
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-{}-daemonsets.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()?;
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let mut kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("daemonsets");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_daemonsets_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_daemonsets_json(json_str: &str) -> Result<Vec<DaemonSetInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let value: Value = serde_json::from_str(json_str)
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.and_then(|i| i.as_array())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut daemonsets = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let desired = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("desiredNumberScheduled"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.unwrap_or(0) as i32;
|
|
|
|
|
|
|
|
|
|
let current = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("currentNumberScheduled"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.unwrap_or(0) as i32;
|
|
|
|
|
|
|
|
|
|
let ready = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("numberReady"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.unwrap_or(0) as i32;
|
|
|
|
|
|
|
|
|
|
let up_to_date = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("updatedNumberScheduled"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.unwrap_or(0) as i32;
|
|
|
|
|
|
|
|
|
|
let available = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("numberAvailable"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.unwrap_or(0) as i32;
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let labels = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("labels"))
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.and_then(|l| l.as_object())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.map(|l| {
|
2026-06-07 05:25:42 +00:00
|
|
|
l.iter()
|
|
|
|
|
.map(|(k, v)| (k.clone(), v.as_str().unwrap_or("").to_string()))
|
|
|
|
|
.collect()
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
})
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
daemonsets.push(DaemonSetInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
desired,
|
|
|
|
|
current,
|
|
|
|
|
ready,
|
|
|
|
|
up_to_date,
|
|
|
|
|
available,
|
|
|
|
|
age,
|
|
|
|
|
labels,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(daemonsets)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_creation_timestamp(timestamp: &str) -> String {
|
|
|
|
|
if timestamp.is_empty() || timestamp == "null" {
|
|
|
|
|
return "N/A".to_string();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Ok(dt) = chrono::DateTime::parse_from_rfc3339(timestamp) {
|
|
|
|
|
let now = chrono::Utc::now();
|
|
|
|
|
let diff = now.signed_duration_since(dt);
|
|
|
|
|
|
|
|
|
|
if diff.num_days() > 0 {
|
|
|
|
|
return format!("{}d", diff.num_days());
|
|
|
|
|
}
|
|
|
|
|
if diff.num_hours() > 0 {
|
|
|
|
|
return format!("{}h", diff.num_hours());
|
|
|
|
|
}
|
|
|
|
|
if diff.num_minutes() > 0 {
|
|
|
|
|
return format!("{}m", diff.num_minutes());
|
|
|
|
|
}
|
|
|
|
|
return format!("{}s", diff.num_seconds());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"N/A".to_string()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
// Resource Management Commands (Phase 2)
|
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn get_pod_logs(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
pod_name: String,
|
|
|
|
|
container_name: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<LogResponse, String> {
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
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-{}-logs.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("logs")
|
|
|
|
|
.arg(pod_name)
|
|
|
|
|
.arg("-n")
|
|
|
|
|
.arg(namespace)
|
|
|
|
|
.arg("-c")
|
|
|
|
|
.arg(container_name)
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let logs = String::from_utf8_lossy(&output.stdout).to_string();
|
|
|
|
|
|
|
|
|
|
Ok(LogResponse { logs })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn scale_deployment(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
deployment_name: String,
|
|
|
|
|
replicas: i32,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<(), String> {
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
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-{}-scale.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("scale")
|
|
|
|
|
.arg("deployment")
|
|
|
|
|
.arg(deployment_name)
|
|
|
|
|
.arg("--replicas")
|
|
|
|
|
.arg(replicas.to_string())
|
|
|
|
|
.arg("-n")
|
|
|
|
|
.arg(namespace)
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.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]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn restart_deployment(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
deployment_name: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<(), String> {
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
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-{}-restart.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("restart")
|
|
|
|
|
.arg("deployment")
|
|
|
|
|
.arg(deployment_name)
|
|
|
|
|
.arg("-n")
|
|
|
|
|
.arg(namespace)
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.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]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn delete_resource(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
resource_type: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
resource_name: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<(), String> {
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
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-{}-delete.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("delete")
|
|
|
|
|
.arg(resource_type)
|
|
|
|
|
.arg(resource_name)
|
|
|
|
|
.arg("-n")
|
|
|
|
|
.arg(namespace)
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.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]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn exec_pod(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
pod_name: String,
|
|
|
|
|
container_name: Option<String>,
|
|
|
|
|
shell: Option<String>,
|
|
|
|
|
command: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<ExecResponse, String> {
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
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-{}-exec.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()?;
|
|
|
|
|
|
2026-06-07 05:25:42 +00:00
|
|
|
const ALLOWED_SHELLS: &[&str] = &[
|
|
|
|
|
"sh",
|
|
|
|
|
"bash",
|
|
|
|
|
"ash",
|
|
|
|
|
"dash",
|
|
|
|
|
"/bin/sh",
|
|
|
|
|
"/bin/bash",
|
|
|
|
|
"/bin/ash",
|
|
|
|
|
"/bin/dash",
|
|
|
|
|
];
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
let shell_cmd = shell.as_deref().unwrap_or("sh");
|
|
|
|
|
if !ALLOWED_SHELLS.contains(&shell_cmd) {
|
2026-06-07 05:25:42 +00:00
|
|
|
return Err(format!(
|
|
|
|
|
"Unsupported shell '{}'; allowed: sh, bash, ash, dash",
|
|
|
|
|
shell_cmd
|
|
|
|
|
));
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
}
|
|
|
|
|
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
let mut cmd = Command::new(kubectl_path);
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
cmd.arg("exec").arg(&pod_name).arg("-n").arg(&namespace);
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
if let Some(ref container) = container_name {
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
cmd.arg("-c").arg(container);
|
|
|
|
|
}
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
cmd.arg("--").arg(shell_cmd).arg("-c").arg(&command);
|
|
|
|
|
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
cmd.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str());
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
|
2026-06-07 05:25:42 +00:00
|
|
|
let output = cmd
|
|
|
|
|
.output()
|
|
|
|
|
.await
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
.map_err(|e| format!("Failed to execute kubectl: {e}"))?;
|
|
|
|
|
|
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
|
|
|
|
|
2026-06-07 05:25:42 +00:00
|
|
|
Ok(ExecResponse {
|
|
|
|
|
stdout,
|
|
|
|
|
stderr,
|
|
|
|
|
exit_code: output.status.code(),
|
|
|
|
|
})
|
feat: implement full Lens-like Kubernetes UI with resource discovery and management
- Add ResourceBrowser with namespace/resource type tabs for pods, services, deployments, statefulsets, daemonsets
- Implement PodList with logs viewer and container selection
- Implement ServiceList with cluster IP, type, ports display
- Implement DeploymentList with scale and restart operations
- Add backend commands: list_namespaces, list_pods, list_services, list_deployments, list_statefulsets, list_daemonsets
- Add resource management commands: get_pod_logs, scale_deployment, restart_deployment, delete_resource, exec_pod
- Add UI components: Table, Tabs, Dialog, Alert to shared UI library
- Update KubernetesPage to use new ResourceBrowser component
- All tests passing (331 Rust + 98 frontend)
- Build successful in release mode
2026-06-07 04:08:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct LogResponse {
|
|
|
|
|
pub logs: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ExecResponse {
|
|
|
|
|
pub stdout: String,
|
|
|
|
|
pub stderr: String,
|
|
|
|
|
pub exit_code: Option<i32>,
|
|
|
|
|
}
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
// Additional Resource Discovery Commands (Phase 3)
|
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ReplicaSetInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub replicas: i32,
|
|
|
|
|
pub ready: String,
|
|
|
|
|
pub age: String,
|
|
|
|
|
pub labels: std::collections::HashMap<String, String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct JobInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub completions: String,
|
|
|
|
|
pub duration: String,
|
|
|
|
|
pub age: String,
|
|
|
|
|
pub labels: std::collections::HashMap<String, String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct CronJobInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub schedule: String,
|
|
|
|
|
pub active: i32,
|
|
|
|
|
pub last_schedule: String,
|
|
|
|
|
pub age: String,
|
|
|
|
|
pub labels: std::collections::HashMap<String, String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ConfigMapInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub data_keys: i32,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct SecretInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
|
pub secret_type: String,
|
|
|
|
|
pub data_keys: i32,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct NodeInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub status: String,
|
|
|
|
|
pub roles: String,
|
|
|
|
|
pub version: String,
|
|
|
|
|
pub internal_ip: String,
|
|
|
|
|
pub external_ip: Option<String>,
|
|
|
|
|
pub os_image: String,
|
|
|
|
|
pub kernel_version: String,
|
|
|
|
|
pub kubelet_version: String,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct EventInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub event_type: String,
|
|
|
|
|
pub reason: String,
|
|
|
|
|
pub object: String,
|
|
|
|
|
pub count: i32,
|
|
|
|
|
pub first_seen: String,
|
|
|
|
|
pub last_seen: String,
|
|
|
|
|
pub message: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct IngressInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub class: Option<String>,
|
|
|
|
|
pub host: String,
|
|
|
|
|
pub addresses: Vec<String>,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct PersistentVolumeClaimInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub status: String,
|
|
|
|
|
pub volume: String,
|
|
|
|
|
pub capacity: String,
|
|
|
|
|
pub access_modes: Vec<String>,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct PersistentVolumeInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub status: String,
|
|
|
|
|
pub capacity: String,
|
|
|
|
|
pub access_modes: Vec<String>,
|
|
|
|
|
pub reclaim_policy: String,
|
|
|
|
|
pub storage_class: String,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ServiceAccountInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub secrets: i32,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct RoleInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ClusterRoleInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct RoleBindingInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub role: String,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ClusterRoleBindingInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub cluster_role: String,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct HorizontalPodAutoscalerInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub min_replicas: i32,
|
|
|
|
|
pub max_replicas: i32,
|
|
|
|
|
pub current_replicas: i32,
|
|
|
|
|
pub desired_replicas: i32,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
feat(kubernetes): implement Lens Desktop v5 feature-parity UI
Complete overhaul of the Kubernetes management page from a basic config
panel into a full Lens-style IDE shell with 26 resource types, real-time
data, and a comprehensive test suite.
Layout & navigation:
- Rewrite KubernetesPage as a Lens v5-style shell: collapsible sidebar
(Workloads / Services & Networking / Config & Storage / Access Control /
Cluster), top hotbar with cluster+namespace selectors, Ctrl+K command
palette
- All 26 resource types now accessible via sidebar navigation (previously 5)
New resource types (Rust + TypeScript + React):
- StorageClasses, NetworkPolicies, ResourceQuotas, LimitRanges
- 4 new Tauri commands registered in generate_handler![]
Component implementations (replacing stubs with real IPC):
- Terminal: full xterm.js with multi-tab sessions and exec_pod IPC
- YamlEditor: Monaco editor with YAML syntax highlighting
- MetricsChart: recharts LineChart/BarChart
- ClusterOverview: live node/pod/deployment/namespace counts
- ClusterDetails: real kubeconfig + node data
- PodDetail, DeploymentDetail, ServiceDetail, ConfigMapDetail, SecretDetail:
all connected to real IPC data, zero hardcoded values
- CreateResourceModal, EditResourceModal: wired to createResourceCmd /
editResourceCmd
- RbacViewer: live data from 4 RBAC IPC commands
- RbacEditor: create roles/cluster-roles via YAML editor
- CommandPalette: 12 real navigation commands, keyboard nav
Dependencies added: xterm@5, xterm-addon-fit, xterm-addon-web-links,
@monaco-editor/react@4, recharts@2
Tooling:
- Replace eslint-plugin-react (incompatible with ESLint 10) with
@eslint-react/eslint-plugin; fix eslint.config.js for flat config
- Fix pre-existing hoisting lint errors in Security.tsx, PortForwardForm.tsx
- Fix eventBus.ts: replace all `any` generics with `unknown`
Tests: 251 passing across 35 test files (was 94/19)
- 16 new test files covering all new and fixed components (TDD)
- npx tsc --noEmit: 0 errors
- cargo clippy -- -D warnings: 0 warnings
- cargo fmt --check: passes
- eslint src/ --max-warnings 0: 0 issues
2026-06-07 21:41:28 +00:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct StorageClassInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub provisioner: String,
|
|
|
|
|
pub reclaim_policy: String,
|
|
|
|
|
pub volume_binding_mode: String,
|
|
|
|
|
pub allow_volume_expansion: bool,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct NetworkPolicyInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub pod_selector: String,
|
|
|
|
|
pub policy_types: Vec<String>,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ResourceQuotaInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub request_cpu: String,
|
|
|
|
|
pub request_memory: String,
|
|
|
|
|
pub limit_cpu: String,
|
|
|
|
|
pub limit_memory: String,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct LimitRangeInfo {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub namespace: String,
|
|
|
|
|
pub limit_count: usize,
|
|
|
|
|
pub age: String,
|
|
|
|
|
}
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_replicasets(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<ReplicaSetInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-replicasets.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("replicasets");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_replicasets_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_replicasets_json(json_str: &str) -> Result<Vec<ReplicaSetInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut replicasets = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let replicas = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("replicas"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.unwrap_or(0) as i32;
|
|
|
|
|
|
|
|
|
|
let ready = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("readyReplicas"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.map(|r| format!("{}/{}", r, replicas))
|
|
|
|
|
.unwrap_or_else(|| format!("0/{}", replicas));
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let labels = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("labels"))
|
|
|
|
|
.and_then(|l| l.as_object())
|
|
|
|
|
.map(|l| {
|
2026-06-07 05:25:42 +00:00
|
|
|
l.iter()
|
|
|
|
|
.map(|(k, v)| (k.clone(), v.as_str().unwrap_or("").to_string()))
|
|
|
|
|
.collect()
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
})
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
replicasets.push(ReplicaSetInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
replicas,
|
|
|
|
|
ready,
|
|
|
|
|
age,
|
|
|
|
|
labels,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(replicasets)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_jobs(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<JobInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-jobs.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("jobs");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_jobs_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_jobs_json(json_str: &str) -> Result<Vec<JobInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut jobs = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let completions = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("succeeded"))
|
|
|
|
|
.and_then(|s| s.as_i64())
|
|
|
|
|
.map(|s| {
|
|
|
|
|
let total = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|sp| sp.get("completions"))
|
|
|
|
|
.and_then(|c| c.as_i64())
|
|
|
|
|
.unwrap_or(1);
|
|
|
|
|
format!("{}/{}", s, total)
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or_else(|| "0/0".to_string());
|
|
|
|
|
|
|
|
|
|
let duration = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("startTime"))
|
|
|
|
|
.and_then(|st| st.as_str())
|
|
|
|
|
.and_then(|st| {
|
|
|
|
|
let completion_time = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("completionTime"))
|
|
|
|
|
.and_then(|ct| ct.as_str());
|
|
|
|
|
completion_time.or(Some(st))
|
|
|
|
|
})
|
|
|
|
|
.map(|st| {
|
|
|
|
|
if let Ok(start) = chrono::DateTime::parse_from_rfc3339(st) {
|
|
|
|
|
let end_time = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("completionTime"))
|
|
|
|
|
.and_then(|ct| ct.as_str());
|
|
|
|
|
if let Some(end) = end_time {
|
|
|
|
|
if let Ok(end_dt) = chrono::DateTime::parse_from_rfc3339(end) {
|
|
|
|
|
let diff = end_dt.signed_duration_since(start);
|
|
|
|
|
if diff.num_minutes() > 0 {
|
|
|
|
|
return format!("{}m", diff.num_minutes());
|
|
|
|
|
}
|
|
|
|
|
return format!("{}s", diff.num_seconds());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let now = chrono::Utc::now();
|
|
|
|
|
let diff = now.signed_duration_since(start);
|
|
|
|
|
if diff.num_minutes() > 0 {
|
|
|
|
|
return format!("{}m", diff.num_minutes());
|
|
|
|
|
}
|
|
|
|
|
return format!("{}s", diff.num_seconds());
|
|
|
|
|
}
|
|
|
|
|
"N/A".to_string()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let labels = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("labels"))
|
|
|
|
|
.and_then(|l| l.as_object())
|
|
|
|
|
.map(|l| {
|
2026-06-07 05:25:42 +00:00
|
|
|
l.iter()
|
|
|
|
|
.map(|(k, v)| (k.clone(), v.as_str().unwrap_or("").to_string()))
|
|
|
|
|
.collect()
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
})
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
jobs.push(JobInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
completions,
|
|
|
|
|
duration,
|
|
|
|
|
age,
|
|
|
|
|
labels,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(jobs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_cronjobs(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<CronJobInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-cronjobs.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("cronjobs");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_cronjobs_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_cronjobs_json(json_str: &str) -> Result<Vec<CronJobInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut cronjobs = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let schedule = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("schedule"))
|
|
|
|
|
.and_then(|s| s.as_str())
|
|
|
|
|
.unwrap_or("* * * * *")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let active = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("active"))
|
|
|
|
|
.and_then(|a| a.as_array())
|
|
|
|
|
.map(|a| a.len() as i32)
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
|
|
|
|
let last_schedule = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("lastScheduleTime"))
|
|
|
|
|
.and_then(|l| l.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let labels = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("labels"))
|
|
|
|
|
.and_then(|l| l.as_object())
|
|
|
|
|
.map(|l| {
|
2026-06-07 05:25:42 +00:00
|
|
|
l.iter()
|
|
|
|
|
.map(|(k, v)| (k.clone(), v.as_str().unwrap_or("").to_string()))
|
|
|
|
|
.collect()
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
})
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
cronjobs.push(CronJobInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
schedule,
|
|
|
|
|
active,
|
|
|
|
|
last_schedule,
|
|
|
|
|
age,
|
|
|
|
|
labels,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(cronjobs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_configmaps(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<ConfigMapInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-configmaps.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("configmaps");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_configmaps_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_configmaps_json(json_str: &str) -> Result<Vec<ConfigMapInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut configmaps = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let data_keys = item
|
|
|
|
|
.get("data")
|
|
|
|
|
.and_then(|d| d.as_object())
|
|
|
|
|
.map(|d| d.len() as i32)
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
configmaps.push(ConfigMapInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
data_keys,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(configmaps)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_secrets(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<SecretInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-secrets.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("secrets");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_secrets_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_secrets_json(json_str: &str) -> Result<Vec<SecretInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut secrets = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let secret_type = item
|
|
|
|
|
.get("type")
|
|
|
|
|
.and_then(|t| t.as_str())
|
|
|
|
|
.unwrap_or("Opaque")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let data_keys = item
|
|
|
|
|
.get("data")
|
|
|
|
|
.and_then(|d| d.as_object())
|
|
|
|
|
.map(|d| d.len() as i32)
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
secrets.push(SecretInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
secret_type,
|
|
|
|
|
data_keys,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(secrets)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_nodes(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<NodeInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-nodes.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("get")
|
|
|
|
|
.arg("nodes")
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_nodes_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_nodes_json(json_str: &str) -> Result<Vec<NodeInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut nodes = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let status = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("conditions"))
|
|
|
|
|
.and_then(|c| c.as_array())
|
|
|
|
|
.and_then(|conditions| {
|
2026-06-07 05:25:42 +00:00
|
|
|
conditions
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|c| c.get("type").and_then(|t| t.as_str()) == Some("Ready"))
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
})
|
|
|
|
|
.and_then(|c| c.get("status").and_then(|s| s.as_str()))
|
|
|
|
|
.map(|s| match s {
|
|
|
|
|
"True" => "Ready",
|
|
|
|
|
"False" => "NotReady",
|
|
|
|
|
_ => "Unknown",
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or("Unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let roles = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("labels"))
|
|
|
|
|
.and_then(|l| l.as_object())
|
|
|
|
|
.map(|l| {
|
|
|
|
|
let mut role_list: Vec<String> = Vec::new();
|
2026-06-07 05:25:42 +00:00
|
|
|
if l.contains_key("node-role.kubernetes.io/control-plane")
|
|
|
|
|
|| l.contains_key("node-role.kubernetes.io/master")
|
|
|
|
|
{
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
role_list.push("control-plane".to_string());
|
|
|
|
|
}
|
|
|
|
|
if l.contains_key("node-role.kubernetes.io/worker") {
|
|
|
|
|
role_list.push("worker".to_string());
|
|
|
|
|
}
|
|
|
|
|
if l.contains_key("node-role.kubernetes.io/etcd") {
|
|
|
|
|
role_list.push("etcd".to_string());
|
|
|
|
|
}
|
|
|
|
|
if l.contains_key("node-role.kubernetes.io/ingress") {
|
|
|
|
|
role_list.push("ingress".to_string());
|
|
|
|
|
}
|
|
|
|
|
if role_list.is_empty() {
|
|
|
|
|
role_list.push("none".to_string());
|
|
|
|
|
}
|
|
|
|
|
role_list.join(",")
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or("none".to_string());
|
|
|
|
|
|
|
|
|
|
let version = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("nodeInfo"))
|
|
|
|
|
.and_then(|n| n.get("kubeletVersion"))
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("N/A")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let internal_ip = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("addresses"))
|
|
|
|
|
.and_then(|a| a.as_array())
|
|
|
|
|
.and_then(|addresses| {
|
2026-06-07 05:25:42 +00:00
|
|
|
addresses
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|addr| addr.get("type").and_then(|t| t.as_str()) == Some("InternalIP"))
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
})
|
|
|
|
|
.and_then(|addr| addr.get("address").and_then(|a| a.as_str()))
|
|
|
|
|
.unwrap_or("N/A")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let external_ip = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("addresses"))
|
|
|
|
|
.and_then(|a| a.as_array())
|
|
|
|
|
.and_then(|addresses| {
|
2026-06-07 05:25:42 +00:00
|
|
|
addresses
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|addr| addr.get("type").and_then(|t| t.as_str()) == Some("ExternalIP"))
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
})
|
|
|
|
|
.and_then(|addr| addr.get("address").and_then(|a| a.as_str()))
|
|
|
|
|
.map(|s| s.to_string());
|
|
|
|
|
|
|
|
|
|
let os_image = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("nodeInfo"))
|
|
|
|
|
.and_then(|n| n.get("osImage"))
|
|
|
|
|
.and_then(|o| o.as_str())
|
|
|
|
|
.unwrap_or("N/A")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let kernel_version = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("nodeInfo"))
|
|
|
|
|
.and_then(|n| n.get("kernelVersion"))
|
|
|
|
|
.and_then(|k| k.as_str())
|
|
|
|
|
.unwrap_or("N/A")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let kubelet_version = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("nodeInfo"))
|
|
|
|
|
.and_then(|n| n.get("kubeletVersion"))
|
|
|
|
|
.and_then(|k| k.as_str())
|
|
|
|
|
.unwrap_or("N/A")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
nodes.push(NodeInfo {
|
|
|
|
|
name,
|
|
|
|
|
status,
|
|
|
|
|
roles,
|
|
|
|
|
version,
|
|
|
|
|
internal_ip,
|
|
|
|
|
external_ip,
|
|
|
|
|
os_image,
|
|
|
|
|
kernel_version,
|
|
|
|
|
kubelet_version,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(nodes)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_events(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: Option<String>,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<EventInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-events.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("events");
|
|
|
|
|
if let Some(ns) = &namespace {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(ns);
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_events_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_events_json(json_str: &str) -> Result<Vec<EventInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut events = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let event_type = item
|
|
|
|
|
.get("type")
|
|
|
|
|
.and_then(|t| t.as_str())
|
|
|
|
|
.unwrap_or("Normal")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let reason = item
|
|
|
|
|
.get("reason")
|
|
|
|
|
.and_then(|r| r.as_str())
|
|
|
|
|
.unwrap_or("Unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let object = item
|
|
|
|
|
.get("involvedObject")
|
|
|
|
|
.and_then(|o| o.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
2026-06-07 05:25:42 +00:00
|
|
|
let count = item.get("count").and_then(|c| c.as_i64()).unwrap_or(1) as i32;
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
|
|
|
|
|
let first_seen = item
|
|
|
|
|
.get("firstTimestamp")
|
|
|
|
|
.and_then(|f| f.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let last_seen = item
|
|
|
|
|
.get("lastTimestamp")
|
|
|
|
|
.and_then(|l| l.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let message = item
|
|
|
|
|
.get("message")
|
|
|
|
|
.and_then(|m| m.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
events.push(EventInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
event_type,
|
|
|
|
|
reason,
|
|
|
|
|
object,
|
|
|
|
|
count,
|
|
|
|
|
first_seen,
|
|
|
|
|
last_seen,
|
|
|
|
|
message,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(events)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_ingresses(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<IngressInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-ingresses.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("ingresses");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_ingresses_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_ingresses_json(json_str: &str) -> Result<Vec<IngressInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut ingresses = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let class = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("ingressClassName"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(|s| s.to_string());
|
|
|
|
|
|
|
|
|
|
let host = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("rules"))
|
|
|
|
|
.and_then(|r| r.as_array())
|
|
|
|
|
.and_then(|rules| rules.first())
|
|
|
|
|
.and_then(|rule| rule.get("host").and_then(|h| h.as_str()))
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let addresses = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("loadBalancer"))
|
|
|
|
|
.and_then(|l| l.get("ingress"))
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.map(|ingress| {
|
2026-06-07 05:25:42 +00:00
|
|
|
ingress
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|ing| {
|
|
|
|
|
ing.get("ip")
|
|
|
|
|
.and_then(|ip| ip.as_str())
|
|
|
|
|
.map(|s| s.to_string())
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
})
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
ingresses.push(IngressInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
class,
|
|
|
|
|
host,
|
|
|
|
|
addresses,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(ingresses)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_persistentvolumeclaims(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<PersistentVolumeClaimInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-pvcs.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("persistentvolumeclaims");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_pvcs_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_pvcs_json(json_str: &str) -> Result<Vec<PersistentVolumeClaimInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut pvcs = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let status = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("phase"))
|
|
|
|
|
.and_then(|p| p.as_str())
|
|
|
|
|
.unwrap_or("Unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let volume = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("volumeName"))
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("N/A")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let capacity = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("capacity"))
|
|
|
|
|
.and_then(|c| c.as_object())
|
|
|
|
|
.map(|c| {
|
|
|
|
|
let storage = c.get("storage").and_then(|s| s.as_str()).unwrap_or("N/A");
|
|
|
|
|
storage.to_string()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let access_modes = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("accessModes"))
|
|
|
|
|
.and_then(|a| a.as_array())
|
|
|
|
|
.map(|modes| {
|
2026-06-07 05:25:42 +00:00
|
|
|
modes
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|m| m.as_str().map(|s| s.to_string()))
|
|
|
|
|
.collect()
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
})
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
pvcs.push(PersistentVolumeClaimInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
status,
|
|
|
|
|
volume,
|
|
|
|
|
capacity,
|
|
|
|
|
access_modes,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(pvcs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_persistentvolumes(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<PersistentVolumeInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-pvs.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("get")
|
|
|
|
|
.arg("persistentvolumes")
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_pvs_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_pvs_json(json_str: &str) -> Result<Vec<PersistentVolumeInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut pvs = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let status = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("phase"))
|
|
|
|
|
.and_then(|p| p.as_str())
|
|
|
|
|
.unwrap_or("Unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let capacity = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("capacity"))
|
|
|
|
|
.and_then(|c| c.as_object())
|
|
|
|
|
.map(|c| {
|
|
|
|
|
let storage = c.get("storage").and_then(|s| s.as_str()).unwrap_or("N/A");
|
|
|
|
|
storage.to_string()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
let access_modes = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("accessModes"))
|
|
|
|
|
.and_then(|a| a.as_array())
|
|
|
|
|
.map(|modes| {
|
2026-06-07 05:25:42 +00:00
|
|
|
modes
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|m| m.as_str().map(|s| s.to_string()))
|
|
|
|
|
.collect()
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
})
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
let reclaim_policy = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("persistentVolumeReclaimPolicy"))
|
|
|
|
|
.and_then(|r| r.as_str())
|
|
|
|
|
.unwrap_or("Retain")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let storage_class = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("storageClassName"))
|
|
|
|
|
.and_then(|s| s.as_str())
|
|
|
|
|
.unwrap_or("N/A")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
pvs.push(PersistentVolumeInfo {
|
|
|
|
|
name,
|
|
|
|
|
status,
|
|
|
|
|
capacity,
|
|
|
|
|
access_modes,
|
|
|
|
|
reclaim_policy,
|
|
|
|
|
storage_class,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(pvs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_serviceaccounts(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<ServiceAccountInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-sas.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("serviceaccounts");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_serviceaccounts_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_serviceaccounts_json(json_str: &str) -> Result<Vec<ServiceAccountInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut serviceaccounts = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let secrets = item
|
|
|
|
|
.get("secrets")
|
|
|
|
|
.and_then(|s| s.as_array())
|
|
|
|
|
.map(|s| s.len() as i32)
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
serviceaccounts.push(ServiceAccountInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
secrets,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(serviceaccounts)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_roles(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<RoleInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-roles.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("roles");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_roles_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_roles_json(json_str: &str) -> Result<Vec<RoleInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut roles = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
roles.push(RoleInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(roles)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_clusterroles(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<ClusterRoleInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-clusterroles.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("get")
|
|
|
|
|
.arg("clusterroles")
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_clusterroles_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_clusterroles_json(json_str: &str) -> Result<Vec<ClusterRoleInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut clusterroles = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
2026-06-07 05:25:42 +00:00
|
|
|
clusterroles.push(ClusterRoleInfo { name, age });
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(clusterroles)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_rolebindings(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<RoleBindingInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-rolebindings.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("rolebindings");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_rolebindings_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_rolebindings_json(json_str: &str) -> Result<Vec<RoleBindingInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut rolebindings = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let role = item
|
|
|
|
|
.get("roleRef")
|
|
|
|
|
.and_then(|r| r.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
rolebindings.push(RoleBindingInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
role,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(rolebindings)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_clusterrolebindings(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<ClusterRoleBindingInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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();
|
2026-06-07 05:25:42 +00:00
|
|
|
let temp_path = temp_dir.join(format!(
|
|
|
|
|
"kubeconfig-{}-clusterrolebindings.yaml",
|
|
|
|
|
cluster_id
|
|
|
|
|
));
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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("get")
|
|
|
|
|
.arg("clusterrolebindings")
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_clusterrolebindings_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_clusterrolebindings_json(json_str: &str) -> Result<Vec<ClusterRoleBindingInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut clusterrolebindings = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let cluster_role = item
|
|
|
|
|
.get("roleRef")
|
|
|
|
|
.and_then(|r| r.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
clusterrolebindings.push(ClusterRoleBindingInfo {
|
|
|
|
|
name,
|
|
|
|
|
cluster_role,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(clusterrolebindings)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn list_horizontalpodautoscalers(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<HorizontalPodAutoscalerInfo>, String> {
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
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-{}-hpas.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("horizontalpodautoscalers");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_hpas_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_hpas_json(json_str: &str) -> Result<Vec<HorizontalPodAutoscalerInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut hpas = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let min_replicas = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("minReplicas"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.unwrap_or(1) as i32;
|
|
|
|
|
|
|
|
|
|
let max_replicas = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("maxReplicas"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.unwrap_or(1) as i32;
|
|
|
|
|
|
|
|
|
|
let current_replicas = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("currentReplicas"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.unwrap_or(0) as i32;
|
|
|
|
|
|
|
|
|
|
let desired_replicas = item
|
|
|
|
|
.get("status")
|
|
|
|
|
.and_then(|s| s.get("desiredReplicas"))
|
|
|
|
|
.and_then(|r| r.as_i64())
|
|
|
|
|
.unwrap_or(0) as i32;
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
hpas.push(HorizontalPodAutoscalerInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
min_replicas,
|
|
|
|
|
max_replicas,
|
|
|
|
|
current_replicas,
|
|
|
|
|
desired_replicas,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(hpas)
|
|
|
|
|
}
|
|
|
|
|
|
feat(kubernetes): implement Lens Desktop v5 feature-parity UI
Complete overhaul of the Kubernetes management page from a basic config
panel into a full Lens-style IDE shell with 26 resource types, real-time
data, and a comprehensive test suite.
Layout & navigation:
- Rewrite KubernetesPage as a Lens v5-style shell: collapsible sidebar
(Workloads / Services & Networking / Config & Storage / Access Control /
Cluster), top hotbar with cluster+namespace selectors, Ctrl+K command
palette
- All 26 resource types now accessible via sidebar navigation (previously 5)
New resource types (Rust + TypeScript + React):
- StorageClasses, NetworkPolicies, ResourceQuotas, LimitRanges
- 4 new Tauri commands registered in generate_handler![]
Component implementations (replacing stubs with real IPC):
- Terminal: full xterm.js with multi-tab sessions and exec_pod IPC
- YamlEditor: Monaco editor with YAML syntax highlighting
- MetricsChart: recharts LineChart/BarChart
- ClusterOverview: live node/pod/deployment/namespace counts
- ClusterDetails: real kubeconfig + node data
- PodDetail, DeploymentDetail, ServiceDetail, ConfigMapDetail, SecretDetail:
all connected to real IPC data, zero hardcoded values
- CreateResourceModal, EditResourceModal: wired to createResourceCmd /
editResourceCmd
- RbacViewer: live data from 4 RBAC IPC commands
- RbacEditor: create roles/cluster-roles via YAML editor
- CommandPalette: 12 real navigation commands, keyboard nav
Dependencies added: xterm@5, xterm-addon-fit, xterm-addon-web-links,
@monaco-editor/react@4, recharts@2
Tooling:
- Replace eslint-plugin-react (incompatible with ESLint 10) with
@eslint-react/eslint-plugin; fix eslint.config.js for flat config
- Fix pre-existing hoisting lint errors in Security.tsx, PortForwardForm.tsx
- Fix eventBus.ts: replace all `any` generics with `unknown`
Tests: 251 passing across 35 test files (was 94/19)
- 16 new test files covering all new and fixed components (TDD)
- npx tsc --noEmit: 0 errors
- cargo clippy -- -D warnings: 0 warnings
- cargo fmt --check: passes
- eslint src/ --max-warnings 0: 0 issues
2026-06-07 21:41:28 +00:00
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn list_storageclasses(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<StorageClassInfo>, 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-{}-storageclasses.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("get")
|
|
|
|
|
.arg("storageclasses")
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat(kubernetes): implement Lens Desktop v5 feature-parity UI
Complete overhaul of the Kubernetes management page from a basic config
panel into a full Lens-style IDE shell with 26 resource types, real-time
data, and a comprehensive test suite.
Layout & navigation:
- Rewrite KubernetesPage as a Lens v5-style shell: collapsible sidebar
(Workloads / Services & Networking / Config & Storage / Access Control /
Cluster), top hotbar with cluster+namespace selectors, Ctrl+K command
palette
- All 26 resource types now accessible via sidebar navigation (previously 5)
New resource types (Rust + TypeScript + React):
- StorageClasses, NetworkPolicies, ResourceQuotas, LimitRanges
- 4 new Tauri commands registered in generate_handler![]
Component implementations (replacing stubs with real IPC):
- Terminal: full xterm.js with multi-tab sessions and exec_pod IPC
- YamlEditor: Monaco editor with YAML syntax highlighting
- MetricsChart: recharts LineChart/BarChart
- ClusterOverview: live node/pod/deployment/namespace counts
- ClusterDetails: real kubeconfig + node data
- PodDetail, DeploymentDetail, ServiceDetail, ConfigMapDetail, SecretDetail:
all connected to real IPC data, zero hardcoded values
- CreateResourceModal, EditResourceModal: wired to createResourceCmd /
editResourceCmd
- RbacViewer: live data from 4 RBAC IPC commands
- RbacEditor: create roles/cluster-roles via YAML editor
- CommandPalette: 12 real navigation commands, keyboard nav
Dependencies added: xterm@5, xterm-addon-fit, xterm-addon-web-links,
@monaco-editor/react@4, recharts@2
Tooling:
- Replace eslint-plugin-react (incompatible with ESLint 10) with
@eslint-react/eslint-plugin; fix eslint.config.js for flat config
- Fix pre-existing hoisting lint errors in Security.tsx, PortForwardForm.tsx
- Fix eventBus.ts: replace all `any` generics with `unknown`
Tests: 251 passing across 35 test files (was 94/19)
- 16 new test files covering all new and fixed components (TDD)
- npx tsc --noEmit: 0 errors
- cargo clippy -- -D warnings: 0 warnings
- cargo fmt --check: passes
- eslint src/ --max-warnings 0: 0 issues
2026-06-07 21:41:28 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_storageclasses_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_storageclasses_json(json_str: &str) -> Result<Vec<StorageClassInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut storageclasses = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let provisioner = item
|
|
|
|
|
.get("provisioner")
|
|
|
|
|
.and_then(|p| p.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let reclaim_policy = item
|
|
|
|
|
.get("reclaimPolicy")
|
|
|
|
|
.and_then(|r| r.as_str())
|
|
|
|
|
.unwrap_or("Delete")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let volume_binding_mode = item
|
|
|
|
|
.get("volumeBindingMode")
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("Immediate")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let allow_volume_expansion = item
|
|
|
|
|
.get("allowVolumeExpansion")
|
|
|
|
|
.and_then(|a| a.as_bool())
|
|
|
|
|
.unwrap_or(false);
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
storageclasses.push(StorageClassInfo {
|
|
|
|
|
name,
|
|
|
|
|
provisioner,
|
|
|
|
|
reclaim_policy,
|
|
|
|
|
volume_binding_mode,
|
|
|
|
|
allow_volume_expansion,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(storageclasses)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn list_networkpolicies(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<NetworkPolicyInfo>, 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-{}-networkpolicies.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("networkpolicies");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat(kubernetes): implement Lens Desktop v5 feature-parity UI
Complete overhaul of the Kubernetes management page from a basic config
panel into a full Lens-style IDE shell with 26 resource types, real-time
data, and a comprehensive test suite.
Layout & navigation:
- Rewrite KubernetesPage as a Lens v5-style shell: collapsible sidebar
(Workloads / Services & Networking / Config & Storage / Access Control /
Cluster), top hotbar with cluster+namespace selectors, Ctrl+K command
palette
- All 26 resource types now accessible via sidebar navigation (previously 5)
New resource types (Rust + TypeScript + React):
- StorageClasses, NetworkPolicies, ResourceQuotas, LimitRanges
- 4 new Tauri commands registered in generate_handler![]
Component implementations (replacing stubs with real IPC):
- Terminal: full xterm.js with multi-tab sessions and exec_pod IPC
- YamlEditor: Monaco editor with YAML syntax highlighting
- MetricsChart: recharts LineChart/BarChart
- ClusterOverview: live node/pod/deployment/namespace counts
- ClusterDetails: real kubeconfig + node data
- PodDetail, DeploymentDetail, ServiceDetail, ConfigMapDetail, SecretDetail:
all connected to real IPC data, zero hardcoded values
- CreateResourceModal, EditResourceModal: wired to createResourceCmd /
editResourceCmd
- RbacViewer: live data from 4 RBAC IPC commands
- RbacEditor: create roles/cluster-roles via YAML editor
- CommandPalette: 12 real navigation commands, keyboard nav
Dependencies added: xterm@5, xterm-addon-fit, xterm-addon-web-links,
@monaco-editor/react@4, recharts@2
Tooling:
- Replace eslint-plugin-react (incompatible with ESLint 10) with
@eslint-react/eslint-plugin; fix eslint.config.js for flat config
- Fix pre-existing hoisting lint errors in Security.tsx, PortForwardForm.tsx
- Fix eventBus.ts: replace all `any` generics with `unknown`
Tests: 251 passing across 35 test files (was 94/19)
- 16 new test files covering all new and fixed components (TDD)
- npx tsc --noEmit: 0 errors
- cargo clippy -- -D warnings: 0 warnings
- cargo fmt --check: passes
- eslint src/ --max-warnings 0: 0 issues
2026-06-07 21:41:28 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_networkpolicies_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_networkpolicies_json(json_str: &str) -> Result<Vec<NetworkPolicyInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut networkpolicies = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let pod_selector = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("podSelector"))
|
|
|
|
|
.map(|ps| serde_json::to_string(ps).unwrap_or_default())
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
let policy_types = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("policyTypes"))
|
|
|
|
|
.and_then(|pt| pt.as_array())
|
|
|
|
|
.map(|types| {
|
|
|
|
|
types
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|t| t.as_str().map(|s| s.to_string()))
|
|
|
|
|
.collect()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
networkpolicies.push(NetworkPolicyInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
pod_selector,
|
|
|
|
|
policy_types,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(networkpolicies)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn list_resourcequotas(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<ResourceQuotaInfo>, 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-{}-resourcequotas.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("resourcequotas");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat(kubernetes): implement Lens Desktop v5 feature-parity UI
Complete overhaul of the Kubernetes management page from a basic config
panel into a full Lens-style IDE shell with 26 resource types, real-time
data, and a comprehensive test suite.
Layout & navigation:
- Rewrite KubernetesPage as a Lens v5-style shell: collapsible sidebar
(Workloads / Services & Networking / Config & Storage / Access Control /
Cluster), top hotbar with cluster+namespace selectors, Ctrl+K command
palette
- All 26 resource types now accessible via sidebar navigation (previously 5)
New resource types (Rust + TypeScript + React):
- StorageClasses, NetworkPolicies, ResourceQuotas, LimitRanges
- 4 new Tauri commands registered in generate_handler![]
Component implementations (replacing stubs with real IPC):
- Terminal: full xterm.js with multi-tab sessions and exec_pod IPC
- YamlEditor: Monaco editor with YAML syntax highlighting
- MetricsChart: recharts LineChart/BarChart
- ClusterOverview: live node/pod/deployment/namespace counts
- ClusterDetails: real kubeconfig + node data
- PodDetail, DeploymentDetail, ServiceDetail, ConfigMapDetail, SecretDetail:
all connected to real IPC data, zero hardcoded values
- CreateResourceModal, EditResourceModal: wired to createResourceCmd /
editResourceCmd
- RbacViewer: live data from 4 RBAC IPC commands
- RbacEditor: create roles/cluster-roles via YAML editor
- CommandPalette: 12 real navigation commands, keyboard nav
Dependencies added: xterm@5, xterm-addon-fit, xterm-addon-web-links,
@monaco-editor/react@4, recharts@2
Tooling:
- Replace eslint-plugin-react (incompatible with ESLint 10) with
@eslint-react/eslint-plugin; fix eslint.config.js for flat config
- Fix pre-existing hoisting lint errors in Security.tsx, PortForwardForm.tsx
- Fix eventBus.ts: replace all `any` generics with `unknown`
Tests: 251 passing across 35 test files (was 94/19)
- 16 new test files covering all new and fixed components (TDD)
- npx tsc --noEmit: 0 errors
- cargo clippy -- -D warnings: 0 warnings
- cargo fmt --check: passes
- eslint src/ --max-warnings 0: 0 issues
2026-06-07 21:41:28 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_resourcequotas_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_resourcequotas_json(json_str: &str) -> Result<Vec<ResourceQuotaInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut resourcequotas = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let hard = item.get("status").and_then(|s| s.get("hard"));
|
|
|
|
|
|
|
|
|
|
let request_cpu = hard
|
|
|
|
|
.and_then(|h| h.get("requests.cpu"))
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let request_memory = hard
|
|
|
|
|
.and_then(|h| h.get("requests.memory"))
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let limit_cpu = hard
|
|
|
|
|
.and_then(|h| h.get("limits.cpu"))
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let limit_memory = hard
|
|
|
|
|
.and_then(|h| h.get("limits.memory"))
|
|
|
|
|
.and_then(|v| v.as_str())
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
resourcequotas.push(ResourceQuotaInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
request_cpu,
|
|
|
|
|
request_memory,
|
|
|
|
|
limit_cpu,
|
|
|
|
|
limit_memory,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(resourcequotas)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn list_limitranges(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<Vec<LimitRangeInfo>, 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-{}-limitranges.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 kubectl_cmd = Command::new(kubectl_path);
|
|
|
|
|
kubectl_cmd.arg("get").arg("limitranges");
|
|
|
|
|
if namespace.is_empty() {
|
|
|
|
|
kubectl_cmd.arg("--all-namespaces");
|
|
|
|
|
} else {
|
|
|
|
|
kubectl_cmd.arg("-n").arg(&namespace);
|
|
|
|
|
}
|
|
|
|
|
let output = kubectl_cmd
|
|
|
|
|
.arg("-o")
|
|
|
|
|
.arg("json")
|
|
|
|
|
.env("KUBECONFIG", temp_path.to_string_lossy().to_string())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat(kubernetes): implement Lens Desktop v5 feature-parity UI
Complete overhaul of the Kubernetes management page from a basic config
panel into a full Lens-style IDE shell with 26 resource types, real-time
data, and a comprehensive test suite.
Layout & navigation:
- Rewrite KubernetesPage as a Lens v5-style shell: collapsible sidebar
(Workloads / Services & Networking / Config & Storage / Access Control /
Cluster), top hotbar with cluster+namespace selectors, Ctrl+K command
palette
- All 26 resource types now accessible via sidebar navigation (previously 5)
New resource types (Rust + TypeScript + React):
- StorageClasses, NetworkPolicies, ResourceQuotas, LimitRanges
- 4 new Tauri commands registered in generate_handler![]
Component implementations (replacing stubs with real IPC):
- Terminal: full xterm.js with multi-tab sessions and exec_pod IPC
- YamlEditor: Monaco editor with YAML syntax highlighting
- MetricsChart: recharts LineChart/BarChart
- ClusterOverview: live node/pod/deployment/namespace counts
- ClusterDetails: real kubeconfig + node data
- PodDetail, DeploymentDetail, ServiceDetail, ConfigMapDetail, SecretDetail:
all connected to real IPC data, zero hardcoded values
- CreateResourceModal, EditResourceModal: wired to createResourceCmd /
editResourceCmd
- RbacViewer: live data from 4 RBAC IPC commands
- RbacEditor: create roles/cluster-roles via YAML editor
- CommandPalette: 12 real navigation commands, keyboard nav
Dependencies added: xterm@5, xterm-addon-fit, xterm-addon-web-links,
@monaco-editor/react@4, recharts@2
Tooling:
- Replace eslint-plugin-react (incompatible with ESLint 10) with
@eslint-react/eslint-plugin; fix eslint.config.js for flat config
- Fix pre-existing hoisting lint errors in Security.tsx, PortForwardForm.tsx
- Fix eventBus.ts: replace all `any` generics with `unknown`
Tests: 251 passing across 35 test files (was 94/19)
- 16 new test files covering all new and fixed components (TDD)
- npx tsc --noEmit: 0 errors
- cargo clippy -- -D warnings: 0 warnings
- cargo fmt --check: passes
- eslint src/ --max-warnings 0: 0 issues
2026-06-07 21:41:28 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let output_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
parse_limitranges_json(&output_str)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_limitranges_json(json_str: &str) -> Result<Vec<LimitRangeInfo>, String> {
|
|
|
|
|
let value: Value = serde_json::from_str(json_str)
|
|
|
|
|
.map_err(|e| format!("Failed to parse kubectl JSON output: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let items = value
|
|
|
|
|
.get("items")
|
|
|
|
|
.and_then(|i| i.as_array())
|
|
|
|
|
.ok_or("Missing 'items' array in kubectl JSON output")?;
|
|
|
|
|
|
|
|
|
|
let mut limitranges = Vec::new();
|
|
|
|
|
for item in items {
|
|
|
|
|
let name = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("name"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("unknown")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let namespace = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("namespace"))
|
|
|
|
|
.and_then(|n| n.as_str())
|
|
|
|
|
.unwrap_or("default")
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
let limit_count = item
|
|
|
|
|
.get("spec")
|
|
|
|
|
.and_then(|s| s.get("limits"))
|
|
|
|
|
.and_then(|l| l.as_array())
|
|
|
|
|
.map(|l| l.len())
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
|
|
|
|
let age = item
|
|
|
|
|
.get("metadata")
|
|
|
|
|
.and_then(|m| m.get("creationTimestamp"))
|
|
|
|
|
.and_then(|c| c.as_str())
|
|
|
|
|
.map(parse_creation_timestamp)
|
|
|
|
|
.unwrap_or("N/A".to_string());
|
|
|
|
|
|
|
|
|
|
limitranges.push(LimitRangeInfo {
|
|
|
|
|
name,
|
|
|
|
|
namespace,
|
|
|
|
|
limit_count,
|
|
|
|
|
age,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(limitranges)
|
|
|
|
|
}
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn cordon_node(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
node_name: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<(), String> {
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
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())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn uncordon_node(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
node_name: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<(), String> {
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
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())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn drain_node(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
node_name: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<(), String> {
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
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())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn rollback_deployment(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
deployment_name: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<(), String> {
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
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())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn create_resource(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
_resource_type: String,
|
|
|
|
|
yaml_content: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<(), String> {
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
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())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
.stdin(Stdio::piped())
|
|
|
|
|
.stdout(Stdio::piped())
|
|
|
|
|
.stderr(Stdio::piped());
|
|
|
|
|
|
2026-06-07 05:25:42 +00:00
|
|
|
let mut child = cmd
|
|
|
|
|
.spawn()
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
.map_err(|e| format!("Failed to spawn kubectl: {e}"))?;
|
|
|
|
|
|
|
|
|
|
if let Some(mut stdin) = child.stdin.take() {
|
2026-06-07 05:25:42 +00:00
|
|
|
stdin
|
|
|
|
|
.write_all(yaml_content.as_bytes())
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
.await
|
|
|
|
|
.map_err(|e| format!("Failed to write yaml to stdin: {e}"))?;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-07 05:25:42 +00:00
|
|
|
let output = child
|
|
|
|
|
.wait_with_output()
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
2026-06-07 05:25:42 +00:00
|
|
|
pub async fn edit_resource(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
_resource_type: String,
|
|
|
|
|
_resource_name: String,
|
|
|
|
|
yaml_content: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<(), String> {
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
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())
|
2026-06-07 23:58:16 +00:00
|
|
|
.arg("--context")
|
|
|
|
|
.arg(context.as_str())
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
.stdin(Stdio::piped())
|
|
|
|
|
.stdout(Stdio::piped())
|
|
|
|
|
.stderr(Stdio::piped());
|
|
|
|
|
|
2026-06-07 05:25:42 +00:00
|
|
|
let mut child = cmd
|
|
|
|
|
.spawn()
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
.map_err(|e| format!("Failed to spawn kubectl: {e}"))?;
|
|
|
|
|
|
|
|
|
|
if let Some(mut stdin) = child.stdin.take() {
|
2026-06-07 05:25:42 +00:00
|
|
|
stdin
|
|
|
|
|
.write_all(yaml_content.as_bytes())
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
.await
|
|
|
|
|
.map_err(|e| format!("Failed to write yaml to stdin: {e}"))?;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-07 05:25:42 +00:00
|
|
|
let output = child
|
|
|
|
|
.wait_with_output()
|
feat: implement additional Kubernetes resource discovery and management commands
- Add 16 new resource discovery commands: replicasets, jobs, cronjobs, configmaps, secrets, nodes, events, ingresses, pvcs, pvs, serviceaccounts, roles, clusterroles, rolebindings, clusterrolebindings, hpas
- Add 6 new management commands: cordon_node, uncordon_node, drain_node, rollback_deployment, create_resource, edit_resource
- All commands follow existing patterns with proper temp file cleanup and error handling
- All tests passing (331 Rust + 98 frontend)
- TypeScript type checks passing
- Build successful in release mode
2026-06-07 05:10:19 +00:00
|
|
|
.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());
|
|
|
|
|
}
|
|
|
|
|
|
fix(kube): resolve automated PR review blockers and warnings
Blockers:
- Replace serde_yaml::from_str with serde_json::from_str in all 6
parse_*_json functions (parse_namespaces, parse_pods, parse_services,
parse_deployments, parse_statefulsets, parse_daemonsets). Update
.as_sequence() → .as_array(), .as_mapping() → .as_object(), and
mapping iterator patterns throughout. Explicitly type serde_yaml::Value
in extract_context/extract_server_url which legitimately parse YAML.
Warnings:
- Add containers: Vec<String> to PodInfo struct; parse from
spec.containers[].name in parse_pods_json
- Fix PodList.tsx to use selectedPod.containers instead of [selectedPod.name]
- Fix exec_pod: add optional shell param with allowlist validation
(sh/bash/ash/dash); correct arg ordering — -c container now placed
before -- separator
- Handle empty namespace with --all-namespaces in all 5 list commands
- Fix dialog overflow: overflow-hidden → overflow-y-auto on inner div
- Memoize namespace options with useMemo in ResourceBrowser
Lint cleanup (all pre-existing, surfaced by eslint config fix):
- Deduplicate eslint.config.js (was doubled to 272 lines); move ignores
to standalone global object; allow console.log in cli section
- Remove stale .eslintignore (migrated to eslint.config.js)
- Remove unused Card/CardTitle imports from Kubernetes list components
- Rename unused props to _clusterId/_namespace in DaemonSetList,
ServiceList, StatefulSetList
- Fix useEffect/useCallback missing deps in Triage and LogUpload
- Remove debug console.log from App.tsx provider auto-test
- Rename unused hover prop to _hover in TableRow (ui/index.tsx)
- Add #[allow(unused_variables)] to Phase 3 stub Tauri commands
- Restore get_pod_logs, scale_deployment, restart_deployment,
delete_resource, exec_pod to lib.rs handler registration (were
accidentally dropped in Phase 3 expansion)
All checks pass: cargo clippy -D warnings, tsc --noEmit,
eslint --max-warnings 0, 331 Rust tests, 98 frontend tests.
2026-06-07 04:55:44 +00:00
|
|
|
Ok(())
|
|
|
|
|
}
|
2026-06-07 15:53:18 +00:00
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn subscribe_to_k8s_events(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
namespace: String,
|
|
|
|
|
resource_type: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<String, String> {
|
|
|
|
|
let _app_state = state.inner();
|
|
|
|
|
|
|
|
|
|
let rx = crate::kube::start_resource_watcher(_app_state, cluster_id, namespace, resource_type)
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|e| format!("Failed to start watcher: {e}"))?;
|
|
|
|
|
|
|
|
|
|
let duration = std::time::SystemTime::now()
|
|
|
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
|
|
|
.map_err(|e| format!("Failed to get duration: {e}"))?;
|
|
|
|
|
let unsubscribe_id = format!("watcher-{}", duration.as_millis());
|
|
|
|
|
|
|
|
|
|
state
|
|
|
|
|
.inner()
|
|
|
|
|
.watchers
|
|
|
|
|
.lock()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.insert(unsubscribe_id.clone(), rx);
|
|
|
|
|
|
|
|
|
|
Ok(unsubscribe_id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn subscribe_to_all_k8s_events(
|
|
|
|
|
cluster_id: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<String, String> {
|
|
|
|
|
let _app_state = state.inner();
|
|
|
|
|
|
|
|
|
|
let rx = crate::kube::start_all_resources_watcher(_app_state, cluster_id)
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|e| format!("Failed to start all watcher: {e}"))?;
|
|
|
|
|
|
|
|
|
|
let duration = std::time::SystemTime::now()
|
|
|
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
|
|
|
.map_err(|e| format!("Failed to get duration: {e}"))?;
|
|
|
|
|
let unsubscribe_id = format!("watcher-all-{}", duration.as_millis());
|
|
|
|
|
|
|
|
|
|
state
|
|
|
|
|
.inner()
|
|
|
|
|
.watchers
|
|
|
|
|
.lock()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.insert(unsubscribe_id.clone(), rx);
|
|
|
|
|
|
|
|
|
|
Ok(unsubscribe_id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
pub async fn unsubscribe_from_k8s_events(
|
|
|
|
|
unsubscribe_id: String,
|
|
|
|
|
state: State<'_, AppState>,
|
|
|
|
|
) -> Result<(), String> {
|
|
|
|
|
let removed = state
|
|
|
|
|
.inner()
|
|
|
|
|
.watchers
|
|
|
|
|
.lock()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.remove(&unsubscribe_id);
|
|
|
|
|
|
|
|
|
|
if removed.is_none() {
|
|
|
|
|
return Err(format!("Watcher {} not found", unsubscribe_id));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|