Skip to content

feat: see & operate on unmanaged kubeconfig clusters across the CLI, desktop app & web UIΒ #5654

Description

@devantler

πŸ€– 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 '<name>' 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).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions