Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- Opt-in dynamic CRD discovery (#197): with `discoverFluxResources: true`,
flux9s watches CRDs labeled `app.kubernetes.io/part-of=flux` — the same
label the Flux Operator's FluxReport reads — and shows their resources
live: unified list with readiness from standard conditions, `:` commands
from the CRD's own names and short names, `y`/`d`, filtering, and pulse
counts. Kinds register and deregister dynamically as CRDs are labeled or
deleted. Discovered kinds are strictly view-only (mutating operations are
gated to built-in Flux kinds), built-in CRDs are excluded from discovery,
and with the flag off (default) no CRD watch runs at all.
- Cluster pulse dashboard (#195): `:pulse` answers "is my GitOps pipeline
healthy?" at a glance — ready/failed/suspended totals and per-kind counts
for the current namespace scope, the most recent failures with their
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ A [K9s](https://github.com/derailed/k9s)-inspired terminal UI for monitoring Flu
- **Controller logs** - Stream any Flux controller pod's logs live with `:logs` (follow, search, bounded buffer)
- **Workload drill-down** - Open a graph workload group to walk Deployments/StatefulSets/DaemonSets: rollout status, containers, pods, events, and pod logs
- **Pulse dashboard** - `:pulse` shows cluster health at a glance: per-kind counts, recent failures, and Flux distribution info
- **CRD discovery (opt-in)** - `discoverFluxResources: true` dynamically shows any CRD labeled `app.kubernetes.io/part-of=flux` (Flagger, tofu-controller, ExternalArtifact-SDK controllers), view-only with generic columns
- **Graph visualization** - Visualize resource relationships and dependencies (Kustomization, HelmRelease, etc.)
- **Reconciliation history** - View reconciliation history for resources that track it
- **Favorites** - Mark frequently accessed resources for quick access
Expand Down
35 changes: 35 additions & 0 deletions docs/content/configuration/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ flux9s config path
| `defaultControllerNamespace` | string | `flux-system` | Namespace where Flux controllers run |
| `defaultResourceFilter` | string | *(none)* | Resource type shown at startup (e.g., `Kustomization`) |
| `connectTimeoutSeconds` | integer | `10` | Startup Kubernetes API health-check timeout in seconds |
| `discoverFluxResources` | boolean | `false` | Opt-in dynamic discovery of Flux-adjacent CRDs (see below) |
| `ui.enableMouse` | bool | `false` | Enable mouse support |
| `ui.headless` | bool | `false` | Hide the header bar |
| `ui.noIcons` | bool | `false` | Disable Unicode icons for terminal compatibility |
Expand Down Expand Up @@ -126,6 +127,40 @@ You can also change the filter interactively during a session by typing `:ks`, `

---

### Discovering Flux-Adjacent Resource Kinds

```yaml
discoverFluxResources: true
```

Off by default. When enabled, flux9s watches CustomResourceDefinitions
labeled `app.kubernetes.io/part-of=flux` — the **same label the Flux
Operator's FluxReport uses** to enumerate reconcilers — and shows their
resources alongside the built-in kinds: they appear in the unified list with
generic columns (readiness from standard conditions), get `:` commands from
the CRD's own names (`:widget`, its plural, and any `kubectl` short names),
and support `y`/`d`. Kinds appear and disappear live as CRDs are labeled or
removed — no restart needed.

To register a kind (for example Flagger's Canary), label its CRD once:

```bash
kubectl label crd canaries.flagger.app app.kubernetes.io/part-of=flux
```

That single label registers it with both the Flux Operator's report and
flux9s.

Guard rails:

- Discovered kinds are **view-only**: suspend, resume, reconcile, and delete
never apply to them
- Built-in Flux kinds are excluded from discovery (the FluxInstance labels
its own CRDs, and those are already watched natively)
- Cluster-scoped CRDs are skipped
- When the flag is off (the default), no CRD watch runs and no extra API
calls are made

### Kubernetes API Connection Timeout

At startup, flux9s probes the Kubernetes API server before starting watchers. If the kubeconfig, context, credentials, network, or API server is not working, flux9s shows a connection error screen instead of hanging indefinitely.
Expand Down
5 changes: 5 additions & 0 deletions docs/content/user-guide/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ You can filter by resource type using commands like:

All resource type commands support autocomplete with `Tab` key.

With [`discoverFluxResources`](../configuration/#discovering-flux-adjacent-resource-kinds)
enabled, CRDs labeled `app.kubernetes.io/part-of=flux` (the Flux Operator's
convention) also get commands here — the kind name, plural, and `kubectl`
short names all work, and the kinds appear in the unified list (view-only).

## Interactive Submenus

Some commands open interactive selection menus when used without arguments, providing an easier way to select from available options.
Expand Down
38 changes: 38 additions & 0 deletions scripts/dev-manifests/simple/00-discovered-crd.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# A minimal custom CRD labeled part-of=flux — the Flux Operator convention
# that flux9s's opt-in CRD discovery (#197, discoverFluxResources) reads.
# Applied first (00- prefix) so the Widget CRs later in the glob order find
# an established CRD.
#
# No status subresource on purpose: the fixture CRs carry their own
# status.conditions so readiness extraction is testable without a controller.
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: widgets.example.com
labels:
app.kubernetes.io/part-of: flux
app.kubernetes.io/managed-by: flux9s-dev
spec:
group: example.com
scope: Namespaced
names:
kind: Widget
plural: widgets
singular: widget
shortNames: [wd]
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
size:
type: string
status:
type: object
x-kubernetes-preserve-unknown-fields: true
34 changes: 34 additions & 0 deletions scripts/dev-manifests/simple/widgets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Widget CRs for the discovered-kind fixture (see 00-discovered-crd.yaml).
# One healthy, one failing — statuses are set in the manifest (the CRD has
# no status subresource), so readiness renders without a real controller.
apiVersion: example.com/v1
kind: Widget
metadata:
name: widget-healthy
namespace: flux-resources
labels: {app.kubernetes.io/managed-by: flux9s-dev}
spec:
size: large
status:
conditions:
- type: Ready
status: "True"
reason: Reconciled
message: Widget is operational
lastTransitionTime: "2026-07-16T00:00:00Z"
---
apiVersion: example.com/v1
kind: Widget
metadata:
name: widget-broken
namespace: flux-resources
labels: {app.kubernetes.io/managed-by: flux9s-dev}
spec:
size: small
status:
conditions:
- type: Ready
status: "False"
reason: ProcessingError
message: widget assembly failed
lastTransitionTime: "2026-07-16T00:00:00Z"
Loading
Loading