Custom Operator Mode

3 min read

spec.customOperator: true declares that this test uses its own operator — not Orkestra’s reconcile loop. Bundle generation and Orkestra helm install/uninstall are skipped. Everything else runs unchanged.


How to activate

Declare it in the spec file. The file is the source of truth — there is no CLI flag.

apiVersion: orkestra.orkspace.io/v1
kind: E2E
metadata:
  name: my-operator-e2e

spec:
  customOperator: true
  crd: ./crd.yaml
  cr: ./cr.yaml
  setup:
    helm:
      - repo: https://charts.example.com
        chart: my-operator
        namespace: my-operator-system
        createNamespace: true
  expect:
    - name: CR creates Deployment
      after: cr-applied
      timeout: 90s
      resources:
        - kind: Deployment
          name: my-app
          namespace: default
          ready: true
    - name: Cleanup verified
      after: cr-deleted
      timeout: 30s
      resources:
        - kind: Deployment
          name: my-app
          namespace: default
          count: 0

What runs, what is skipped

StepNormalcustomOperator: true
Cluster setup
CRD apply (spec.crd)
Setup manifests (spec.setup)
Bundle generate + applyskipped
Orkestra helm installskipped
OCI import pre-pullskipped
CR apply
Expectations + assertions
Orkestra helm uninstallskipped
CRD / setup cleanup

spec.katalog is optional when customOperator: true. If omitted, the test has no katalog — only spec.crd, spec.cr, and spec.setup are used.


The setup.helm pattern

Almost always paired with setup.helm to install the operator before the CR is applied. The setup section installs the operator; assertions check what it creates.

spec:
  customOperator: true
  setup:
    helm:
      - repo: https://charts.jetstack.io
        chart: cert-manager
        namespace: cert-manager
        createNamespace: true
        version: v1.14.0
        values:
          installCRDs: true
    wait:
      - kind: Deployment
        name: cert-manager
        namespace: cert-manager
        ready: true
        timeout: 120s

Use cases

Migration parity testing

You are migrating from a Kubebuilder operator to Orkestra. The old operator is still in production. Write two e2e files — one with Orkestra, one with customOperator + setup.helm installing your existing binary — and use identical assertions in both. When both pass, the migration is verified.

my-operator/
├── e2e-orkestra.yaml        # spec.katalog: ./katalog.yaml
└── e2e-kubebuilder.yaml     # spec.customOperator: true
                             # setup.helm: my-old-operator-chart
                             # same assertions as e2e-orkestra.yaml

Third-party operator smoke tests

Test the behavior of operators you did not write — FluxCD, cert-manager, Crossplane, ArgoCD. Install via setup.helm, apply a CR, assert what the operator creates.

spec:
  customOperator: true
  crd: ./certificate-crd.yaml
  cr: ./cr-certificate.yaml
  setup:
    helm:
      - repo: https://charts.jetstack.io
        chart: cert-manager
        # ...
  expect:
    - name: Certificate issued
      after: cr-applied
      timeout: 60s
      resources:
        - kind: Secret
          name: my-tls-secret
          namespace: default

Two-operator composition

Install two operators via setup.helm, apply CRs for both, assert they interact correctly. Neither needs to be Orkestra.

spec:
  customOperator: true
  setup:
    helm:
      - repo: https://operator-a.example.com
        chart: operator-a
      - repo: https://operator-b.example.com
        chart: operator-b
  cr: ./cr-combined.yaml
  expect:
    - name: Operator A and B outputs composed
      after: cr-applied
      timeout: 120s
      commands:
        - run: kubectl get compositeresource my-resource -o jsonpath='{.status.ready}'
          outputContains: "true"

ork e2e as a universal test harness

The assertion infrastructure — polling loops, count checks, command assertions, cleanup verification — is valuable regardless of which operator framework is under test. customOperator: true exposes it to any team writing Kubernetes operators: controller-runtime, Operator SDK, kube-rs, kopf, or anything else. They all get the same CI harness as first-class Orkestra users.


Example suite

For two runnable examples, run:

ork init --pack use-cases/custom-operator
cd use-cases/custom-operator
ExampleWhat it shows
01-pure-customcustomOperator: true with a Kubebuilder-style operator installed via setup.helm
02-side-by-sideThe same CR tested twice — once with Orkestra, once with a custom operator — identical assertions

→ Back: 04-imports | Schema index