gameplane / docs
DEVELOP

Operator and CRD development

Evolve declarative APIs and reconciliation safely, including generated schemas, RBAC, chart copies, and envtest.

Component Guidesv0.218 MIN

Gameplane uses Kubernetes Custom Resource Definitions (CRDs) and controller-runtime reconcilers to manage cluster state declaratively. This guide covers the CRD authoring workflow, reconciliation patterns, and testing strategies.

Helm does not auto-upgrade CRDs

Generated chart CRDs and explicit upgrade instructions must stay synchronized. After editing a CRD, update the chart copies and document any schema changes that affect existing resources.

Controller contract

Reconcile idempotently from spec to owned resources and actionable, current status. A reconciler must be safe to re-run at any time without changing the outcome.

Use owner references and finalizers deliberatelyOwner references establish resource ownership; finalizers protect against premature deletion.
Never treat controller-owned status as desired inputStatus reflects observed state, not user intent. Users define spec; the controller sets status based on current observations.
Report observedGeneration, phase, conditions, reason, and messageStatus fields let clients distinguish transient errors from permanent failures and track reconciliation progress.

Conditions should follow the Kubernetes conventions: each condition reports one aspect of the resource’s state (type), with status (True/False/Unknown), reason (machine-readable code), and message (human-readable detail). The observedGeneration field tracks which spec generation is reflected in status; when observedGeneration < metadata.generation, the controller hasn’t yet reconciled the latest spec changes.

Change a CRD

Go types and Kubebuilder markers are the source of truth; generated code, schemas, RBAC, and chart copies are reviewed artifacts that must stay in sync.

Edit operator/api/v1alpha1 types and compatibility-conscious markersAdd or modify structs and marker comments (e.g., `//+kubebuilder:validation:Required`). Keep marker tags concise and only change them if the schema semantics change.
Run make generate for deepcopy and make manifests for CRDs/RBACThese commands regenerate the deepcopy code, CRD manifests, and RBAC rules. Never hand-edit the output files (`zz_generated.deepcopy.go`, `operator/config/crd/*.yaml`).
Review operator/config/crd and charts/gameplane/crds togetherThe chart copies must match the source CRDs verbatim. The Makefile's `make manifests` target runs `hack/sync-chart-crds.sh` to synchronize them automatically, but verify the diff.

The zz_generated.deepcopy.go file is regenerated by controller-gen (make generate). The CRD YAML files in operator/config/crd/ are the canonical copies; charts/gameplane/crds/ and charts/gameplane/crd-manifests/ receive copies stamped with a bundle hash for upgrade tracking. Always commit generated files in the same changeset as the type changes.

Test reconciliation

Use unit tests for helpers, envtest for API/controller behavior, and Kind E2E for real scheduling, networking, storage, images, and Jobs.

  • Unit tests (_test.go in the same directory as the code) exercise pure Go logic: helper functions, validation, string parsing, and error handling. Run with go test ./....
  • Envtest (_envtest_test.go with //go:build envtest build tags) spins up a temporary Kubernetes cluster with the operator and API server installed. Tests can create real CRD resources, trigger reconciliation, and assert on reconciled state. Envtest is faster than Kind (no container images to build or push) and ideal for controller logic and integration between operator and API.
  • Kind E2E (test/e2e/*_test.go with //go:build e2e build tags) runs against a fully deployed cluster with all components running in pods. E2E tests verify real scheduling decisions, networking, storage provisioning, and job execution — scenarios where envtest’s mock API cannot fully simulate production behavior.

CRD LOOP

01   01 make generate # Deepcopy code and schemas
02   02 make manifests # CRDs, RBAC, and chart copies
03   03 make test-integration # Kubernetes envtest

Run make test-integration locally before pushing. It will download the Kubernetes control-plane binaries (envtest) and run all envtest-tagged tests for the operator and API modules. CI runs the full test suite including E2E; do not skip this step locally — wait for CI results to confirm the change works across all tiers.

Workflow summary

  1. Design: Sketch the CRD fields, enum values, and validation rules in your spec struct.
  2. Implement: Add Kubebuilder markers (validation, RBAC, OpenAPI schema directives) alongside the type definitions.
  3. Generate: Run make generate && make manifests. Review the diffs to ensure no unexpected schema changes.
  4. Test: Write unit tests for helpers, envtest tests for reconciler logic and edge cases.
  5. Commit: Include types, generated code, CRD manifests, and tests in one commit with a clear message explaining the new capability or fix.
  6. Deploy: Push to a feature branch. CI will run the full test suite (unit + envtest + E2E) before the change can be merged to main.