> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Status Scale Printer Columns Short Names

> Explains how to configure a WebApp CRD so it behaves like native Kubernetes workloads by enabling status and scale subresources, printer columns, and categories

Previously we had a working WebApp custom resource: CRD, schema, validation, and a controller. Functionally it worked, but it didn't behave like a first-class Kubernetes resource:

* `kubectl get wa` showed only NAME and AGE.
* `kubectl scale wa` failed.
* A regular `kubectl apply` could overwrite `.status` because the API server wasn't preventing writes to it.

Below is the minimal CRD we started from:

```yaml theme={null}
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: webapps.webapp.kodekloud.com
spec:
  group: webapp.kodekloud.com
  scope: Namespaced
  names:
    plural: webapps
    singular: webapp
    kind: WebApp
    listKind: WebAppList
    shortNames: [wa]
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
```

In this guide we add four CRD fields that make your WebApp resource behave like a built-in workload:

* Enable the `status` subresource so only the `/status` endpoint (typically controllers) can update `.status`.
* Enable the `scale` subresource so `kubectl scale` and HPAs can target the CR.
* Add `additionalPrinterColumns` so `kubectl get wa` shows meaningful columns.
* Add `categories: [all]` so tools that honor categories treat your CR as a top-level workload.

No controller code changes or rebuilds are required — the API server enforces these behaviors based purely on the CRD.

## Quick overview: What each CRD field does

| CRD Field                  | Purpose                                   | Effect                                                                                |
| -------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------- |
| `schema` includes `status` | Prevents server-side pruning of `.status` | Controllers can write status; ordinary writes will not persist status fields          |
| `subresources.status`      | Exposes `/status` endpoint                | Only `/status` updates `.status` (prevents accidental writes via `apply`)             |
| `subresources.scale`       | Exposes `/scale` endpoint                 | `kubectl scale` and HPAs can update `.spec.replicas` and read `.status.readyReplicas` |
| `additionalPrinterColumns` | Customize `kubectl get` output            | Shows meaningful columns like Image, Replicas, Ready, Port, Age                       |
| `names.categories`         | Categorize the CR                         | Tools (and `kubectl api-resources --categories=all`) include it in tooling groups     |

***

## Step 1 — Teach the schema about `.status` and tighten `.spec` validation

Add a `status` object to the OpenAPI schema so the API server does not prune it. Also make `spec` validation explicit (required fields, types and ranges):

```yaml theme={null}
# fragment: spec.versions[0].schema.openAPIV3Schema
type: object
properties:
  spec:
    type: object
    required: [image, replicas]
    properties:
      image:
        type: string
      replicas:
        type: integer
        minimum: 1
        maximum: 10
      port:
        type: integer
        default: 8080
  status:
    type: object
    properties:
      readyReplicas:
        type: integer
      labelSelector:
        type: string
```

This schema change ensures:

* The API server preserves `.status` (it won't be removed by pruning).
* Clients get clear validation errors for missing or invalid `spec` fields.

## Step 2 — Enable `/status` and add the `scale` subresource

Turn on the `/status` endpoint by adding an empty `status: {}` under `subresources`. Add the `scale` block and point it to the JSONPaths for desired and observed replicas and for the label selector:

```yaml theme={null}
# fragment: spec.versions[0].subresources
subresources:
  status: {}
  scale:
    specReplicasPath: .spec.replicas
    statusReplicasPath: .status.readyReplicas
    labelSelectorPath: .status.labelSelector
```

With this in place:

* `kubectl scale wa <name> --replicas=<n>` is routed to the scale subresource and updates `.spec.replicas`.
* HorizontalPodAutoscalers (HPAs) that target this resource can read replicas and update the scale endpoint.

<Callout icon="lightbulb" color="#1CB2FE">
  Enabling the `status` subresource prevents ordinary `apply`/`update` requests from modifying `.status`. Only the `/status` endpoint (used by controllers) may change status fields.
</Callout>

## Step 3 — Add additional printer columns for `kubectl get`

Define columns so `kubectl get wa` displays useful information. Each column needs `name`, `type`, and a `jsonPath`. Use `priority: 1` to hide less important columns unless `-o wide` is requested:

```yaml theme={null}
# fragment: spec.versions[0].additionalPrinterColumns
additionalPrinterColumns:
  - name: Image
    type: string
    jsonPath: .spec.image
    priority: 1
  - name: Replicas
    type: integer
    jsonPath: .spec.replicas
  - name: Ready
    type: integer
    jsonPath: .status.readyReplicas
  - name: Port
    type: integer
    jsonPath: .spec.port
  - name: Age
    type: date
    jsonPath: .metadata.creationTimestamp
```

These columns make `kubectl get wa` feel like a native resource listing (Image, Replicas, Ready, Port, Age).

## Step 4 — Add `categories: [all]`

Add `categories: [all]` under `spec.names` so tools that respect categories (and `kubectl api-resources --categories=all`) include the resource among top-level workload types:

```yaml theme={null}
# fragment: spec.names
names:
  plural: webapps
  singular: webapp
  kind: WebApp
  listKind: WebAppList
  shortNames: [wa]
  categories: [all]
```

Note: `kubectl get all` is a hard-coded convenience command and will not list CRDs even if they declare `categories: [all]`. The category is still useful for other tooling and `kubectl api-resources --categories=all`.

<Callout icon="warning" color="#FF6B6B">
  `kubectl get all` will not list your CRs even if `categories: [all]` is set. Use `kubectl api-resources --categories=all` or `kubectl get webapps` / `kubectl get wa` to view your resources.
</Callout>

***

## Final consolidated CRD (relevant fragments)

Below is a consolidated view showing the key fields you should include. Fill in the remaining CRD metadata as appropriate for your project.

```yaml theme={null}
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: webapps.webapp.kodekloud.com
spec:
  group: webapp.kodekloud.com
  scope: Namespaced
  names:
    plural: webapps
    singular: webapp
    kind: WebApp
    listKind: WebAppList
    shortNames: [wa]
    categories: [all]
  versions:
    - name: v1
      served: true
      storage: true
      subresources:
        status: {}
        scale:
          specReplicasPath: .spec.replicas
          statusReplicasPath: .status.readyReplicas
          labelSelectorPath: .status.labelSelector
      additionalPrinterColumns:
        - name: Image
          type: string
          jsonPath: .spec.image
          priority: 1
        - name: Replicas
          type: integer
          jsonPath: .spec.replicas
        - name: Ready
          type: integer
          jsonPath: .status.readyReplicas
        - name: Port
          type: integer
          jsonPath: .spec.port
        - name: Age
          type: date
          jsonPath: .metadata.creationTimestamp
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              required: [image, replicas]
              properties:
                image:
                  type: string
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 10
                port:
                  type: integer
                  default: 8080
            status:
              type: object
              properties:
                readyReplicas:
                  type: integer
                labelSelector:
                  type: string
```

## Apply the CRD

No API server restart is required — changes to CRDs are picked up immediately.

```bash theme={null}
$ kubectl apply -f webapp-crd.yaml
customresourcedefinition.apiextensions.k8s.io/webapps.webapp.kodekloud.com configured
```

After applying:

* `kubectl get wa` will show the extra columns you defined. (The `Image` column may be hidden unless you use `-o wide` because it has `priority: 1`.)
* `kubectl scale wa <name> --replicas=<n>` updates `.spec.replicas` via the scale subresource.
* Regular `kubectl apply` / `kubectl update` attempts that include `.status` will see `.status` fields dropped — only `/status` updates persist status.

## Validate category registration

```bash theme={null}
$ kubectl api-resources --categories=all | grep -E 'NAME|webapp'
NAME       SHORTNAMES   APIVERSION                 NAMESPACED   KIND
webapps    wa           webapp.kodekloud.com/v1    true         WebApp
```

## Summary

By adding these CRD fields:

* `schema` for `status` and `spec` validation
* `subresources.status`
* `subresources.scale`
* `additionalPrinterColumns`
* `names.categories: [all]`

you make your custom resource behave like a first-class Kubernetes workload. All of the enforcement is handled by the API server from the CRD itself — you do not need to change or rebuild the controller.

References:

* [Kubebuilder](https://kubebuilder.io) can generate these CRD fields automatically from Go marker comments on your types.

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/kubernetes-operators/module/ba392f08-9d70-442b-9751-fdc2052b777e/lesson/e548531a-9522-478c-a1f2-5b2d4467a0c3" />

  <Card title="Practice Lab" icon="flask-conical" cta="Learn more" href="https://learn.kodekloud.com/user/courses/kubernetes-operators/module/ba392f08-9d70-442b-9751-fdc2052b777e/lesson/bdaa4a6c-ee26-4b84-8fcf-1f0074e15286" />
</CardGroup>
