diff --git a/api/v1alpha1/nodepool_types.go b/api/v1alpha1/nodepool_types.go index 647167f..cf64934 100644 --- a/api/v1alpha1/nodepool_types.go +++ b/api/v1alpha1/nodepool_types.go @@ -266,6 +266,7 @@ type NodePoolStatus struct { // +kubebuilder:object:root=true // +kubebuilder:resource:scope=Cluster,shortName=np // +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status` // +kubebuilder:printcolumn:name="Strategy",type=string,JSONPath=`.spec.strategy` // +kubebuilder:printcolumn:name="Providers",type=string,JSONPath=`.status.providers` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` diff --git a/config/crd/bases/nebula.inftyai.com_nodepools.yaml b/config/crd/bases/nebula.inftyai.com_nodepools.yaml index b084570..9fb4152 100644 --- a/config/crd/bases/nebula.inftyai.com_nodepools.yaml +++ b/config/crd/bases/nebula.inftyai.com_nodepools.yaml @@ -17,10 +17,13 @@ spec: scope: Cluster versions: - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Status + type: string - jsonPath: .spec.strategy name: Strategy type: string - - jsonPath: .spec.providers[*].name + - jsonPath: .status.providers name: Providers type: string - jsonPath: .metadata.creationTimestamp @@ -296,6 +299,12 @@ spec: Placed counts existing instances per provider (booting included), for at-a-glance balance. type: object + providers: + description: |- + Providers is a comma-separated list of provider names from the pool + spec. kubectl printcolumns cannot join array fields via JSONPath, so + the controller materializes this summary for `kubectl get nodepool`. + type: string type: object type: object served: true diff --git a/docs/architecture.md b/docs/architecture.md index 49d9adf..c4030e0 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -406,6 +406,12 @@ Responsibilities: - compute `status.placed` from Bound NodeClaims per provider; - watch NodeClaims so placement counts update as instances come and go. +The default `kubectl get nodepools` table exposes the `Ready` condition's value +as `STATUS`, followed by strategy, providers, and age. The CRD printer column +reads the condition directly, so `status.conditions` remains the source of truth. +See the [printer-column design](design/nodepool-status-column.md) for the empty +condition and compatibility behavior. + Static spec rules are admission-time CEL validations. Examples: `Weighted` requires a weight on every provider entry, and AWS provider entries require at least one region. @@ -502,6 +508,7 @@ spec: failover: blocklistTTL: 30s status: + providers: modal,aws placed: modal: 2 aws: 1 diff --git a/docs/deploy.md b/docs/deploy.md index 663618f..8c4c08d 100644 --- a/docs/deploy.md +++ b/docs/deploy.md @@ -197,7 +197,12 @@ diff <(kubectl get secret nebula-webhook-server-cert -n nebula-system -o jsonpat A pool referencing an unregistered provider shows it plainly: ```bash -kubectl get nodepool -o jsonpath='{.status.conditions}' +kubectl get nodepools +# NAME STATUS STRATEGY PROVIDERS AGE +# gpu-pool False Ordered modal,aws 2m + +# Inspect the condition reason and message when STATUS is False. +kubectl get nodepool -o jsonpath='{.status.conditions[?(@.type=="Ready")]}' # Ready=False / UnknownProvider means that provider's creds are missing or wrong. ``` diff --git a/docs/design/nodepool-status-column.md b/docs/design/nodepool-status-column.md new file mode 100644 index 0000000..85501de --- /dev/null +++ b/docs/design/nodepool-status-column.md @@ -0,0 +1,46 @@ +# NodePool status printer column + +## Context + +`NodePool.status.conditions` already reports whether a pool can be used. The +controller owns a standard `Ready` condition and sets it to `True` for a valid +pool or `False` when an environment-dependent validation, such as provider +registration, fails. However, the default `kubectl get nodepools` table does not +show that signal, so operators must request the full object or write a JSONPath. + +## Decision + +Add a `Status` CRD printer column whose JSONPath selects the status of the +`Ready` condition: + +```text +.status.conditions[?(@.type=="Ready")].status +``` + +The column is derived directly by the Kubernetes API server when it renders the +table. No duplicate status field or controller change is introduced. This keeps +the condition as the single source of truth and uses the standard condition +values `True`, `False`, and `Unknown`. + +The column appears before policy details so pool health is visible immediately: + +```text +NAME STATUS STRATEGY PROVIDERS AGE +gpu-pool True Ordered modal,runpod 2m +``` + +Before the controller has written the `Ready` condition, the table cell has no +value. This is preferable to manufacturing a fourth status value because absence +already means the controller has not observed the object. + +## Compatibility and rollout + +This is an additive change to `additionalPrinterColumns`; the stored and served +resource schema is unchanged. Existing clients that read `NodePool` objects are +unaffected. Installing the regenerated CRD is sufficient to enable the column +for existing pools, and the next `kubectl get` uses their existing conditions. + +## Verification + +Generation is checked into `config/crd/bases`. Regenerating the manifests keeps +the CRD printer column aligned with the marker in `nodepool_types.go`.