feat: phase 2 — CRDs, monitoring, port forwarding, bulk delete, and metrics improvements - #5
Merged
Merged
Conversation
Discover all CRDs on the cluster and browse/inspect their instances using fabric8's generic resource API. Separate from the static ResourceType enum since CRDs are dynamic. - CrdRepository wraps fabric8 CRD discovery and generic resource APIs - CrdDefinition and CustomResourceSummary data models - CrdListScreen groups CRDs by API group with search - CrdInstanceListScreen shows instances with pull-to-refresh and search - CrdInstanceDetailScreen with Overview (flattened spec/status) and YAML tabs - Extract YamlView composable from ResourceDetailScreen for reuse - Add CRD navigation drawer item and routes - MainViewModel exposes crdRepository StateFlow
Two-tier monitoring: WorkManager periodic checks (15-min baseline) + optional ForegroundService with real-time fabric8 watches. - ClusterAlert and AlertType model for notification events - AlertStateStore with 1-hour dedup cooldown via SharedPreferences - NotificationHelper creates channels and builds alert/service notifications - ClusterMonitorWorker checks pods/nodes/deployments on 15-min schedule - ClusterWatchService foreground service with live Kubernetes watches - MonitoringPreferences DataStore for monitoring/real-time/alert toggles - MonitoringSettingsScreen with toggles and POST_NOTIFICATIONS handling - Register service in manifest with foregroundServiceType=dataSync - Add monitoring drawer item and navigation route
Local TCP forwarding from device to cluster pods/services via fabric8's portForward() API. Sessions persist across screen navigation via app-scoped manager. - PortForwardSession model with status tracking (Starting/Active/Failed/Stopped) - PortForwardManager as app-scoped singleton with health check polling - PortForwardService foreground service keeping process alive - PortForwardScreen with pod/service selector, port dropdown, active session list - Service-to-pod resolution via label selector matching - Copy localhost:port to clipboard, stop/remove individual forwards - TopAppBar badge showing active forward count - Port Forward action in Pod/Service detail overflow menu - MainViewModel exposes KubernetesClient StateFlow for forwarding - Stop forwards for old connection on connection change
Update adaptive icon to use color resource background (#232837) instead of bitmap, and refresh all density launcher images.
…ults Show named ports (e.g. "8080 (http)") in pod/service/port dropdowns, auto-select when only one resource exists, auto-fill local port to match remote, and fix missing pre-selected service port auto-fill.
CRDs are resources like any other category, so they belong in the bottom navigation alongside Workloads, Network, Config, Storage, and Cluster rather than buried in the drawer with settings items.
The previous approach set savedStateHandle on the source entry but read from the destination entry. Switch to query parameters on the route so pre-selected pod/service values actually reach the port forward screen.
The port forward screen was hardcoded to "default" when no namespace was selected, so only resources in the default namespace appeared in the dropdowns. Now passes the active namespace directly (empty = all namespaces). Also uses each resource's own namespace when starting a forward instead of the screen-level namespace.
The foreground service monitor checked activeCount immediately on start, but activeCount only counted Active sessions — not Starting ones. Since the async port forward hadn't connected yet, the count was 0 and the service stopped itself, calling stopAll() which wiped the session before it could finish connecting. Fix activeCount to include Starting sessions, and make stopAll() skip sessions already in a terminal state (Failed/Stopped).
fabric8's portForward() operates on the pod and needs the container port, not the service port. A service with port 80 -> targetPort 8080 was passing 80 to portForward(), which failed because no container listens on 80. Now extracts targetPort from ServicePort.targetPort and uses it for the actual forward. The dropdown shows the mapping (e.g. "80 -> 8080 (http)") so the user sees both ports.
Android can't bind to privileged ports (<1024), so auto-filling the local port to match the remote port (e.g. 80) caused SocketException. Now assigns a random port in the 10000-59999 range. Also adds an open-in-browser button on active sessions and improves error messages to include the exception class and cause chain.
Long-press any resource to enter selection mode with checkboxes, tap to toggle items, then bulk delete with confirmation dialog and snackbar feedback. Selection clears on type/namespace/search changes and back press.
Add interval picker (1s, 2s, 5s, 10s, 30s) to the metrics screen using FilterChips. Default changed from 5s to 2s. Buffer size adjusts dynamically to maintain a 5-minute window at any interval.
Shorten the chart window from 5 minutes to 60 seconds so data points scroll by more visibly. X-axis now shows seconds ago (e.g. "60s", "30s", "0s") instead of raw indices.
- Log bulk delete failures instead of silently swallowing exceptions - Clear selection state on screen dispose to prevent stale UI - Centralize window slot calculation in MetricsCollector companion - Add defensive bounds checking on x-axis formatter - Fix missing space in message parameter assignments
The previous implementation used fabric8's watchLog() which relies on HTTP streaming that doesn't work reliably on Android — the InputStream/OutputStream approaches both fail to deliver new log lines after the initial tail batch. Replace with a polling approach: fetch initial lines with tailingLines + usingTimestamps, then poll every 2s with sinceTime to get only new lines. Timestamps are used to deduplicate across polls since sinceTime is inclusive.
When navigating away and back, startStreaming() cancels the old job and starts a new one. The old job's catch(Exception) block was catching CancellationException, setting isStreaming=false and showing "StandaloneCoroutine was cancelled" — overwriting the new job's state. Rethrow CancellationException so it's treated as normal coroutine cancellation, not an error.
nuttingd
enabled auto-merge (squash)
February 2, 2026 05:53
nuttingd
disabled auto-merge
February 2, 2026 05:56
|
🎉 This PR is included in version 1.2.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 2 feature set for Kexplore, adding several major capabilities and quality-of-life improvements.
Custom Resource Definitions (CRDs)
Cluster Monitoring & Notifications
Port Forwarding
Multi-Select Bulk Delete
Metrics Improvements
Log Tailing Fixes
watchLog()streaming with polling-based approach usinggetLog()+sinceTime()deduplication — fixes logs never updating in real-timeCancellationExceptionerror shown when navigating away from and back to the log screenOther
Test plan