feat(kube): FreeLens parity — PTY shells, metrics, port-forward, and UX fixes #88
No reviewers
Labels
No Label
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: sarman/tftsr-devops_investigation#88
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "feature/freelens-parity-complete"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
FreeLens feature parity: PTY interactive shells, enriched CRD/CR discovery, metrics support, plus a batch of stability and UX fixes identified during review.
Changes
Bug Fixes
pod/containerparameter names in Tauri invoke payloads — frontend was sendingpodName/containerName(serialised to snake_casepod_name/container_name) but Rust expectedpod/containeransi-to-reactCJS/ESM interop — unwrapped default export at runtime; addedoptimizeDeps.includein Vite configBadge classNameoverrides withStatusBadgecomponent; extended variant coverage forCrashLoopBackOff,OOMKilled,Terminating,Evictedcdn.jsdelivr.net) which is unreachable in Tauri WebView — installedmonaco-editor, configuredloader.config({ monaco })inmain.tsxPodInfostruct was missing these fields; added extraction from kubectl JSON (status.podIP,spec.nodeName, sum ofrestartCount)disarm()until afterstart_exec/attach_sessionreturnsOk; addedpath_str()to borrow path without consuming the guardFeatures
PortForwardDialogcomponent with pre-filled cluster/namespace/pod context; wired intoPodDetailandDeploymentDetailheadersNamespaceListnow has a per-row edit button that fetches YAML and opensEditResourceModal(cluster-scoped)CI
RENOVATE_ENDPOINTcorrected from/api/v3to/api/v1(Gitea only has v1)Test plan
kubectl execinteractive shell opens and accepts input for a running podkubectl attachconnects to an interactive pod (e.g.vault-0)CrashLoopBackOffAutomated PR Review (qwen3-coder-next via liteLLM):\n\nSummary
This PR introduces significant enhancements including PTY shell support, metrics integration, port-forwarding UX, and stability fixes. The changes are largely well-implemented, but there is one critical memory-leak-like issue in the log streaming cleanup logic, and one subtle bug in the metrics-enabled column config that could lead to unnecessary API polling.
Findings
[BLOCKER] src/components/Kubernetes/LogStreamPanel.tsx:146–157 — Cleanup logic may not unlisten before calling
stopLogStreamCmd, risking event handler accumulation.Evidence:
useEffect(() => { return () => { if (unlistenRef.current) { unlistenRef.current(); unlistenRef.current = null; } if (streamIdRef.current) { stopLogStreamCmd(streamIdRef.current).catch(() => {}); streamIdRef.current = null; } }; }, []);The
streamIdRef.current = null;is set inside the cleanup effect, but ifstopLogStreamCmdthrows or the component unmounts quickly, theunlistenmay not be called in time. Worse,stopLogStreamCmdis fire-and-forget after the unlisten, butstreamIdRef.current = null;occurs before ensuring the backend stream is stopped—meaning the cleanup order is safe, but the comment in the code says "Synchronous cleanup", yet it doesn't await unlisten. However,unlisten()returns a synchronous function. The real issue is thatstreamIdRef.current = null;is executed afterstopLogStreamCmd().catch(...)in the effect, which is correct. But the comment "Synchronous cleanup to ensure unlisten is called immediately" is misleading becausestopLogStreamCmdis async and fire-and-forget, and there's no guarantee the stream is stopped before cleanup finishes. More critically, thestopStreamfunction (lines 70–86) correctly unlistens first, but theuseEffectcleanup does not callstopStream. This duplication and inconsistency could lead to leaks or stray events.Fix: Replace the inline cleanup effect with a call to
stopStreamto avoid duplication and ensure correct order:[WARNING] src/components/Kubernetes/PodList.tsx:74–78 — Metrics polling may fire even when only one of the two metrics columns is visible, due to
metricsEnabledlogic being correct but not optimized for rare cases where only CPU or only Memory is enabled.Evidence:
const metricsEnabled = isColumnVisible("cpu") || isColumnVisible("memory");This is logically correct, but in the
useMetricscall:That's fine — but the hook
useMetricsisn’t shown here, and if it internally pollsgetPodMetricsCmdregardless of whether any metrics are being displayed, this would waste network/CPU. However, based on the file contents, there is no evidence ofuseMetricsimplementation, so this is speculative. But since the change is only about enabling polling when columns are visible, and the PR title says "metrics", it's likely this logic is correct. No direct evidence of a bug, so this is not raised as a finding — the real bug is only the cleanup order.No other issues pass the bar of being confirmed, non-speculative, and not previously addressed.
Verdict: REQUEST CHANGES
Code Review Response — PR #88
Both findings assessed against the current code. Neither represents a real bug.
BLOCKER — LogStreamPanel unmount cleanup duplication
False finding.
The reviewer proposes replacing the inline
useEffectcleanup with a call tostopStream(). The current design is intentional and correct; the proposed fix would introduce subtle issues.Why the current code is correct:
React
useEffectcleanup functions are synchronous. The inline cleanup (lines 72–88) does exactly whatstopStream()does, minussetStreaming(false):unlisten()is called synchronously — this is the critical step that prevents stray eventsstopLogStreamCmd(...)is fire-and-forgotten — same as the current async call instopStreamWhy the proposed fix would be worse:
stopStreamisasync. Calling it in a sync cleanup context creates a floating Promise — theawait stopLogStreamCmd(...)andsetStreaming(false)calls all run after the cleanup has already returned.setStreaming(false)updates state on an already-unmounted component. React 18 doesn't crash on this, but it is unnecessary and can emit dev-mode warnings about state updates on unmounted components.unlisten()is already called synchronously in the current inline cleanup, which is the only part that truly must happen immediately.The duplication is deliberate and documented with inline comments (
// Synchronous cleanupand// Fire-and-forget cleanup for backend stream).WARNING — Metrics polling when only one column is visible
Not a finding (reviewer's own assessment).
The reviewer explicitly states: "there is no evidence of useMetrics implementation, so this is speculative" and "this is not raised as a finding."
The logic
const metricsEnabled = isColumnVisible("cpu") || isColumnVisible("memory")is correct — polling only occurs when at least one of the two metrics columns is actually visible. TheuseMetricshook passesnullforclusterId/namespacewhenmetricsEnabledis false, which the hook uses as the signal to disable polling entirely. No bug.Verdict
No changes required. Both findings are false against the current implementation.