Complete Example

2 min read

A Website CRD with all three status layers declared.

CRD definition

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: websites.demo.orkestra.io
spec:
  group: demo.orkestra.io
  versions:
    - name: v1alpha1
      served: true
      storage: true
      subresources:
        status: {}
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                image:
                  type: string
                replicas:
                  type: integer
            status:
              type: object
              x-kubernetes-preserve-unknown-fields: true
  names:
    kind: Website
    plural: websites
    singular: website
  scope: Namespaced

Katalog

- name: website
  crdFile: website-crd.yaml

  operatorBox:
    default: true

    status:
      fields:
        # Layer 2 — from the CR spec
        - path: phase
          value: "Running"
        - path: observedReplicas
          value: "{{ .spec.replicas }}"
        - path: endpoint
          value: "{{ .metadata.name }}.{{ .metadata.namespace }}.svc.cluster.local"

        # Layer 3 — from child resource status
        - path: readyReplicas
          value: "{{ get .children.deployment \"status\" \"readyReplicas\" }}"
        - path: availableReplicas
          value: "{{ get .children.deployment \"status\" \"availableReplicas\" }}"

    onCreate:
      deployments:
        - image: "{{ .spec.image }}"
          replicas: "{{ .spec.replicas }}"
          reconcile: true
      services:
        - port: "80"
          targetPort: "80"
          reconcile: true

After a successful reconcile with two ready replicas:

status:
  conditions:
    - type: Ready
      status: "True"
      reason: ReconcileSucceeded
      message: ""
      lastTransitionTime: "2026-03-29T09:02:33Z"
      observedGeneration: 1
  observedGeneration: 1
  phase: Running
  observedReplicas: "2"
  endpoint: my-site.orkestra.svc.cluster.local
  readyReplicas: "2"
  availableReplicas: "2"

Status in hooks

Go hooks have full access to the CR object and the Kubernetes client. Write status directly from a hook:

func (r *websiteReconciler) OnReconcile(ctx context.Context, obj *apiv1.Website) error {
    // ... reconcile logic ...

    obj.Status.Phase = "Running"
    obj.Status.ReadyReplicas = actualReadyCount

    _, err := r.client.Resource(websiteGVR).
        Namespace(obj.Namespace).
        UpdateStatus(ctx, toUnstructured(obj), metav1.UpdateOptions{})
    return err
}

Hooks and declarative status fields are not mutually exclusive. When both are declared, Orkestra applies the declarative fields after the hook completes — the hook’s writes are preserved (the patch is merged, not replaced).


Disabling status management

To disable all automatic status management for a CRD:

operatorBox:
  status:
    conditions: false
    fields: []

With both disabled, Orkestra makes no status patches. The CR’s status is entirely managed by Go hooks or left empty.