10-constructor/

directory
v0.3.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 30, 2026 License: Apache-2.0

README

10 — Custom Constructor

When you need maximal control — migrate existing operators, run state machines, or own the reconcile loop.

Orkestra is declarative first. Most operators — even complex ones — are fully expressible in a Katalog using when: conditions, dependencies, status fields, and hook templates. Only when you have an existing controller‑runtime operator that you want to bring in as‑is, or when you need complete control over the reconcile loop (e.g., a custom state machine not easily described in YAML), do you reach for a custom constructor.


The three layers of Orkestra

Layer What you write Use case
Declarative Katalog YAML (status, when, resources) 95% of operators
Typed Hooks Go functions (OnReconcile, OnDelete) Adding complex external calls, custom logic inside the safe loop
Custom Constructor Full domain.Reconciler implementation Migrating existing controllers, state machines that own the loop

Try declarative first. If you can’t, try hooks. Only then consider a constructor.


What a constructor gives you – and what you lose

Feature Declarative / Hooks Constructor
Finalizer management Automatic You handle
Status (Ready condition) Automatic You write
Declared status fields Written automatically Ignored
Kubernetes events Automatic You emit
Drift correction (onReconcile templates) Yes You implement
Panic recovery Yes Yes (still safe)
Metrics (reconcile duration, errors) Yes Yes
Worker pool, queue, informer Yes Yes

Constructor gives you full control over the reconcile method. Use it when you already have an existing reconciler and want to plug it into Orkestra without rewriting.


How to migrate an existing controller‑runtime operator

Suppose you have a standard controller‑runtime reconciler:

// Existing code – controller‑runtime
func (r *PipelineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    // your logic here
    return ctrl.Result{RequeueAfter: 10 * time.Second}, nil
}

To move it to Orkestra:

  1. Change the signature to Reconcile(ctx context.Context, key string) error
    • key is namespace/name (same as req.String())
    • Return nil for done, error to requeue with exponential backoff
  2. Remove the manager setup – Orkestra provides the informer, queue, and metrics.
  3. Register your constructor in the Katalog with reconciler.default: false
  4. Run ork generate registry to wire it into Orkestra.

That’s it. Your existing reconcile logic runs inside Orkestra’s runtime infrastructure.


When you might still want a constructor (migration aside)

  • State machines with many phases – you could also do this declaratively with when: conditions, but a constructor lets you centralise the state transition logic.
  • Complex external API dependencies – if the hook model’s OnReconcile is too restrictive (e.g., you need to requeue conditionally based on external state), a constructor gives you full control.
  • Gradual adoption – you have a large codebase that already implements Reconcile, and you want to run it under Orkestra while you later refactor parts into hooks.

Otherwise, use declarative Katalog or typed hooks.


The example: Pipeline state machine

This example demonstrates a constructor that runs a series of Jobs (build → test → notify). It is intentionally written as a constructor to show the pattern. But the same behaviour can be expressed declaratively in the Katalog. We provide the constructor version for learning and migration reference.


Files in this pack

.
├── api/v1alpha/           ← Pipeline CRD Go types
├── reconciler/            ← custom reconciler implementation
│   └── reconciler.go      ← NewPipelineReconciler + Reconcile logic
├── cmd/orkestra/          ← main.go (imports generated registry)
├── pkg/runtime/           ← generated registry (after `make registry`)
├── katalog.yaml
├── Makefile
├── Dockerfile
└── crd.yaml, cr.yaml

Step 1 – Generate the registry

make registry

This creates pkg/runtime/zz_generated_runtime_registry.go, which registers your CRD types and the NewPipelineReconciler constructor.


Step 2 – Enable the registry in main.go

In cmd/orkestra/main.go, uncomment the blank import:

import (
    _ "github.com/workspace/orkestra-pipeline-demo/pkg/runtime"
    // ...
)

Step 3 – Build your custom binary

First, see the expected error with the standard ork CLI:

ork validate -k katalog.yaml
# error: no reconciler constructor registered for Kind=Pipeline

Now build your own binary:

make clean
make build
cp ~/.orkestra/bin/ork ./ork

Validate with your binary:

./ork validate -k katalog.yaml   # passes

Step 4 – Run locally

kind create cluster --name ork-pipeline
kubectl apply -f crd.yaml
./ork run -k katalog.yaml

In another terminal:

kubectl apply -f cr.yaml
kubectl get pipeline -w
kubectl get jobs -w

You’ll see the state machine step through buildtestnotify.


Step 5 – Deploy to a cluster

make release IMAGE=yourregistry/pipeline-operator:v1
./ork generate bundle -k katalog.yaml -o bundle.yaml
kubectl apply -f bundle.yaml
helm repo add orkestra https://ialexeze.github.io/orkestra
helm install orkestra orkestra/orkestra \
  --set runtime.image.repository=yourregistry/pipeline-operator \
  --set runtime.image.tag=v1 \
  --namespace orkestra-system --create-namespace
kubectl apply -f crd.yaml
kubectl apply -f cr.yaml

Cleanup

kind delete cluster --name ork-pipeline
helm uninstall orkestra -n orkestra-system
kubectl delete -f bundle.yaml

Next steps

  • If you have an existing controller‑runtime operator, try migrating it using this pattern.
  • For new operators, start with the declarative Katalog (example 01–08). Only reach for a constructor if you truly need to own the reconcile loop.

Directories

Path Synopsis
reconciler/pipeline_reconciler.go
reconciler/pipeline_reconciler.go
api/v1alpha1/pipeline_types.go
api/v1alpha1/pipeline_types.go

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL