devantler-tech / devantler-tech/ksail
feat: see & operate on unmanaged kubeconfig clusters across the CLI, desktop app & web UI
- Dominant language
- Go
- Stars
- 165
- Forks
- 12
- Avg merge
- 5h 41m
- Merged PRs (30d)
- 337
Description
> 🤖 Generated by the Daily AI Assistant
## Problem
ksail only "sees" clusters that it discovers through its **infrastructure providers** (Docker / Hetzner / Omni / AWS-EKS / nested-Kubernetes) plus in-flight lifecycle jobs — `Service.List()` in `pkg/cli/clusterapi/local_service.go`, which backs both the CLI (`ksail cluster list`) and the web UI / desktop app. A cluster that already exists in the user's kubeconfig but was **not** provisioned by ksail (a managed EKS/GKE/AKS cluster, a kubeadm cluster, a colleague's cluster, etc.) is **invisible** everywhere — even though most of ksail's read/operate surface already works against any context via client-go.
## Goal
ksail should be able to **see and operate on any kubeconfig cluster**, across **both the CLI and the desktop app / web UI**, within the limitations of what it can do for a cluster it does **not** manage. Unmanaged (kubeconfig-only) clusters are shown **clearly and visibly marked as unmanaged / unsupported** — never hidden, never presented as a normal managed cluster, and never silently failing when a ksail-only action is attempted.
## What already works generically (any kubeconfig context, via client-go)
Resource browsing, pod logs & exec, `kubectl`-style access, server-side manifest apply, kubeconfig export, apiserver proxy/watch, and secrets cipher are all client-go / local-key backed and need no per-cluster ksail metadata — so they should "just work" against an imported cluster once ksail can select it.
## Proposed direction
1. **One cluster model, marked.** Extend cluster enumeration (`Service.List()` / `enumerate`) to also read the user's kubeconfig (`clientcmd`) and synthesize a `v1alpha1.Cluster` for each context **not** already discovered via a provider — keyed by context name and flagged **unmanaged / kubeconfig-only**. Distribution/provider are best-effort hints (context-name / server-URL heuristics) and left empty when unknown. This one model feeds **both** the CLI and the UI, so behaviour is identical across surfaces. The existing injectable `kubeconfigPath` seam makes it unit-testable with a fixture kubeconfig.
2. **CLI surfaces them, marked.** See the CLI parity section below.
3. **UI surfaces them, marked.** An "Add / import from kubeconfig" affordance in `ClusterFormDialog.tsx`; render unmanaged clusters in `ClustersTable.tsx` / `ClusterSwitcher.tsx` with a clear **unmanaged** badge and a graceful `—` for unknown distribution/provider.
4. **Gate ksail-only actions with the existing capability flags** (no silent failures) — reusing the flags the local backend already emits (`clusterUpdate`, component-install, start/stop, …); no new flags expected.
## CLI parity — `ksail cluster` and `ksail workload`
The same "see + operate within limits, clearly marked unmanaged" model applies to the CLI. `ksail cluster list` and `ksail cluster switch` are the headline cases; the rest of the `cluster` and `workload` groups follow the same rule:
- **See / select (work on any kubeconfig context) — surface with an explicit `unmanaged` / `unsupported` marker:**
- `ksail cluster list` — include kubeconfig contexts not managed by ksail, with an explicit `unmanaged` status column (never hidden, never shown as a normal managed cluster).
- `ksail cluster switch` — allow switching the active context to an unmanaged cluster (it is just a kubeconfig context switch), noting it is unmanaged.
- `ksail cluster info` / `ksail cluster connect` — show/emit what client-go can (endpoint, kubeconfig access), with unknown ksail-only fields shown as `—`.
- **Operate within limits (client-go-backed — work on the selected context):** `ksail workload {kubectl, watch, network, edit, images, push, render, validate, scan, debug}` and `ksail cluster diagnose` should work against the selected unmanaged context. GitOps-driven commands (`ksail workload reconcile`, `ksail workload install`) depend on ksail/GitOps config and should clearly report "requires a ksail-managed / GitOps-configured cluster" when that config is absent — not a stack trace.
- **Manage / lifecycle (ksail-only → refuse clearly on unmanaged):** `ksail cluster {create, delete, update, start, stop, backup, restore, repair, ttl}` must fail fast with a documented message (e.g. `cluster '' is not managed by ksail; lifecycle operations are unavailable for imported kubeconfig clusters`) — a clean, documented exit, never a stack trace or a silent no-op.
Consistency: the CLI `unmanaged` marker and the UI "unmanaged" badge are the **same** concept driven by the one marked cluster model, so a cluster behaves identically whether the user is on the CLI or in the UI.
## Limitations to make clearly & visibly unavailable on unmanaged (kubeconfig-only) clusters
These require ksail-managed provisioning / `spec.cluster.*` metadata, so they are shown as explicitly disabled/refused with a reason (UI: disabled control + tooltip; CLI: documented error):
- **Lifecycle operations** (create / delete / start / stop) — ksail has no provider or distribution to drive them.
- **Component installation** (CNI, CSI, metrics-server, GitOps/Flux, policy engine) — all driven from `spec.cluster.*`.
- **Reconfigure / re-provision & provider credentials** — no provider spec to act on.
- **GitOps operations** (reconcile / install) — need ksail/GitOps config, not just a kubeconfig.
- **Distribution / provider** shown as best-effort or `—` (no ksail spec to read).
- **Live status reconciliation** (phase / endpoint / nodesReady) — limited to what client-go can directly observe, without the operator's reconciler.
## Acceptance criteria
- `ksail cluster list` and the UI cluster list both show kubeconfig contexts not managed by ksail, clearly marked `unmanaged` / imported (CLI status column + UI badge).
- `ksail cluster switch` can select an unmanaged cluster; the client-go-backed read/operate commands (and their UI equivalents — resource browsing, logs/exec, manifest apply, proxy/watch) work against it.
- Every ksail-only action (lifecycle, component install, GitOps, reprovision) is **visibly disabled/refused with an explanation** on unmanaged clusters — never a silent failure, a bare 501, or an unhandled stack trace.
- No regression for ksail-managed clusters (they keep full capability across CLI + UI).
- Docs (CLI reference for `cluster`/`workload`, plus web UI / desktop) document unmanaged-cluster tracking and its visible-limitations model.
## Suggested decomposition (independently-shippable children)
1. **Backend enumeration + marked model** (natural first slice): kubeconfig-context enumeration in `Service.List()` → synthetic `v1alpha1.Cluster` with an **unmanaged** marker + a distribution-detection heuristic + tests via the `kubeconfigPath` fixture seam. Audit `spec.cluster.distribution` / `provider` optionality in `pkg/apis/cluster/v1alpha1/types.go` (`omitempty` / schema). This model is shared by all downstream children.
2. **CLI: see / select** — `ksail cluster list` shows the `unmanaged` marker; `ksail cluster switch` / `info` / `connect` operate on unmanaged contexts; `ksail cluster diagnose` + the client-go `workload` commands work against the selected context.
3. **CLI: refuse lifecycle clearly** — `ksail cluster {create,delete,update,start,stop,backup,restore,repair,ttl}` and GitOps `workload {reconcile,install}` return the documented "not managed by ksail" error for unmanaged clusters.
4. **Capability gating & API contract** — imported clusters report the right capability flags so ksail-only actions are disabled + labelled; confirm the `pkg/webui/api` contract stays cluster-agnostic.
5. **Web UI** — import affordance in `ClusterFormDialog.tsx` + unmanaged badge + graceful degraded rendering in `ClustersTable.tsx` / `ClusterSwitcher.tsx` (the SSE cluster-list stream already carries generic cluster objects, so `useClusterStream.ts` / `api.ts` likely need no change).
6. **Docs** — document the unmanaged-cluster tracking capability and the visible-limitations model across CLI + UI.
## Size
**Large (epic)** — decomposes into the small children above; the backend-enumeration + marked-model child (1) is the natural first increment and unblocks both the CLI (2/3) and UI (5) work.
## Change surface (grounded on `main`)
- **Backend / model:** `pkg/cli/clusterapi/local_service.go` (`List` / `enumerate` / `newCluster`), `pkg/apis/cluster/v1alpha1/types.go` (`ClusterSpec` optionality), `pkg/webui/api` (contract).
- **CLI:** `pkg/cli/cmd/cluster/{list,switch,info,connect,diagnose}.go` (surface + select), `pkg/cli/cmd/cluster/{create,delete,update,startstop,backup,restore,repair,ttl}.go` (refuse cleanly), `pkg/cli/cmd/workload/{reconcile,install}.go` (GitOps guard).
- **UI:** `web/ui/src/components/{ClustersTable,ClusterSwitcher,ClusterFormDialog}.tsx`, `web/ui/src/api.ts` (likely no change).
---
Part of the KSail-as-a-Kubernetes-UI direction (see roadmap #4988).
Contributor guide
Research direction
Start with pkg/cli/clusterapi/local_service.go, especially List, enumerate, and newCluster, then inspect the kubeconfigPath fixture seam and pkg/apis/cluster/v1alpha1/types.go for optional distribution/provider fields. The first increment is complete when kubeconfig-only contexts are enumerated as marked synthetic clusters with tests, without changing managed-cluster behavior; later work covers the listed CLI and UI files.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, kubernetes, typescript
- Domain
- backend, cli, devops, frontend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100