feature/freelens-parity-complete #87

Merged
sarman merged 16 commits from feature/freelens-parity-complete into master 2026-06-10 01:06:11 +00:00
5 changed files with 68 additions and 68 deletions
Showing only changes of commit 16fdde20b2 - Show all commits

View File

@ -6402,6 +6402,7 @@ pub async fn list_custom_resources(
/// Simple JSONPath-like extractor for custom resource fields.
/// Supports basic paths like .status.phase, .spec.replicas, .metadata.labels['app']
#[allow(dead_code)]
fn extract_json_path_value(item: &Value, json_path: &str) -> String {
// Remove leading dot if present
let path = json_path.strip_prefix('.').unwrap_or(json_path);

View File

@ -310,17 +310,18 @@ pub async fn start_pty_exec_session(
.map_err(|e| format!("kubectl not found: {e}"))?;
// Start session
let params = crate::shell::session::SessionParams {
cluster_id,
namespace,
pod,
container,
kubectl_path: kubectl_path.to_string_lossy().to_string(),
kubeconfig_path,
};
let session_id = state
.pty_sessions
.start_exec_session(
app,
cluster_id,
namespace,
pod,
container,
kubectl_path.to_string_lossy().to_string(),
kubeconfig_path,
)
.start_exec_session(app, params)
.await
.map_err(|e| format!("Failed to start exec session: {e}"))?;
@ -368,17 +369,18 @@ pub async fn start_pty_attach_session(
.map_err(|e| format!("kubectl not found: {e}"))?;
// Start session
let params = crate::shell::session::SessionParams {
cluster_id,
namespace,
pod,
container,
kubectl_path: kubectl_path.to_string_lossy().to_string(),
kubeconfig_path,
};
let session_id = state
.pty_sessions
.start_attach_session(
app,
cluster_id,
namespace,
pod,
container,
kubectl_path.to_string_lossy().to_string(),
kubeconfig_path,
)
.start_attach_session(app, params)
.await
.map_err(|e| format!("Failed to start attach session: {e}"))?;

View File

@ -48,11 +48,27 @@ pub enum ControlCommand {
Terminate,
}
/// Parameters for starting a session
pub struct SessionParams {
pub cluster_id: String,
pub namespace: String,
pub pod: String,
pub container: Option<String>,
pub kubectl_path: String,
pub kubeconfig_path: Option<String>,
}
/// Global session registry
pub struct SessionManager {
sessions: Arc<RwLock<HashMap<String, SessionInfo>>>,
}
impl Default for SessionManager {
fn default() -> Self {
Self::new()
}
}
impl SessionManager {
pub fn new() -> Self {
Self {
@ -64,32 +80,24 @@ impl SessionManager {
pub async fn start_exec_session(
&self,
app_handle: AppHandle,
cluster_id: String,
namespace: String,
pod: String,
container: Option<String>,
kubectl_path: String,
kubeconfig_path: Option<String>,
params: SessionParams,
) -> Result<String> {
let session_id = Uuid::now_v7().to_string();
// Spawn PTY session
let pty_session = PtySession::spawn_kubectl_exec(
&kubectl_path,
&namespace,
&pod,
container.as_deref(),
kubeconfig_path.as_deref(),
&params.kubectl_path,
&params.namespace,
&params.pod,
params.container.as_deref(),
params.kubeconfig_path.as_deref(),
)
.context("Failed to spawn kubectl exec session")?;
self.register_session(
app_handle,
session_id.clone(),
cluster_id,
namespace,
pod,
container,
params,
SessionType::Exec,
pty_session,
)
@ -102,32 +110,24 @@ impl SessionManager {
pub async fn start_attach_session(
&self,
app_handle: AppHandle,
cluster_id: String,
namespace: String,
pod: String,
container: Option<String>,
kubectl_path: String,
kubeconfig_path: Option<String>,
params: SessionParams,
) -> Result<String> {
let session_id = Uuid::now_v7().to_string();
// Spawn PTY session
let pty_session = PtySession::spawn_kubectl_attach(
&kubectl_path,
&namespace,
&pod,
container.as_deref(),
kubeconfig_path.as_deref(),
&params.kubectl_path,
&params.namespace,
&params.pod,
params.container.as_deref(),
params.kubeconfig_path.as_deref(),
)
.context("Failed to spawn kubectl attach session")?;
self.register_session(
app_handle,
session_id.clone(),
cluster_id,
namespace,
pod,
container,
params,
SessionType::Attach,
pty_session,
)
@ -141,10 +141,7 @@ impl SessionManager {
&self,
app_handle: AppHandle,
session_id: String,
cluster_id: String,
namespace: String,
pod: String,
container: Option<String>,
params: SessionParams,
session_type: SessionType,
pty_session: PtySession,
) -> Result<()> {
@ -153,10 +150,10 @@ impl SessionManager {
let info = SessionInfo {
id: session_id.clone(),
cluster_id,
namespace,
pod,
container,
cluster_id: params.cluster_id,
namespace: params.namespace,
pod: params.pod,
container: params.container,
session_type,
created_at: chrono::Utc::now(),
stdin_tx,

View File

@ -43,7 +43,7 @@ export function InteractiveAttachModal({
const terminalRef = useRef<HTMLDivElement>(null);
const xtermRef = useRef<XTerminal | null>(null);
const fitAddonRef = useRef<FitAddon | null>(null);
const [sessionId, setSessionId] = useState<string | null>(null);
const sessionIdRef = useRef<string | null>(null);
const [error, setError] = useState<string | null>(null);
const unlistenOutputRef = useRef<UnlistenFn | null>(null);
const unlistenClosedRef = useRef<UnlistenFn | null>(null);
@ -81,7 +81,7 @@ export function InteractiveAttachModal({
pod,
container
);
setSessionId(sid);
sessionIdRef.current = sid;
// Listen for output from backend
const unlistenOutput = await listen<number[]>(
@ -144,8 +144,8 @@ export function InteractiveAttachModal({
if (unlistenErrorRef.current) {
unlistenErrorRef.current();
}
if (sessionId) {
terminatePtySessionCmd(sessionId).catch(console.error);
if (sessionIdRef.current) {
terminatePtySessionCmd(sessionIdRef.current).catch(console.error);
}
term.dispose();
fitAddon.dispose();
@ -169,8 +169,8 @@ export function InteractiveAttachModal({
}, []);
const handleClose = () => {
if (sessionId) {
terminatePtySessionCmd(sessionId).catch(console.error);
if (sessionIdRef.current) {
terminatePtySessionCmd(sessionIdRef.current).catch(console.error);
}
onClose();
};

View File

@ -43,7 +43,7 @@ export function InteractiveShellModal({
const terminalRef = useRef<HTMLDivElement>(null);
const xtermRef = useRef<XTerminal | null>(null);
const fitAddonRef = useRef<FitAddon | null>(null);
const [sessionId, setSessionId] = useState<string | null>(null);
const sessionIdRef = useRef<string | null>(null);
const [error, setError] = useState<string | null>(null);
const unlistenOutputRef = useRef<UnlistenFn | null>(null);
const unlistenClosedRef = useRef<UnlistenFn | null>(null);
@ -81,7 +81,7 @@ export function InteractiveShellModal({
pod,
container
);
setSessionId(sid);
sessionIdRef.current = sid;
// Listen for output from backend
const unlistenOutput = await listen<number[]>(
@ -144,8 +144,8 @@ export function InteractiveShellModal({
if (unlistenErrorRef.current) {
unlistenErrorRef.current();
}
if (sessionId) {
terminatePtySessionCmd(sessionId).catch(console.error);
if (sessionIdRef.current) {
terminatePtySessionCmd(sessionIdRef.current).catch(console.error);
}
term.dispose();
fitAddon.dispose();
@ -169,8 +169,8 @@ export function InteractiveShellModal({
}, []);
const handleClose = () => {
if (sessionId) {
terminatePtySessionCmd(sessionId).catch(console.error);
if (sessionIdRef.current) {
terminatePtySessionCmd(sessionIdRef.current).catch(console.error);
}
onClose();
};