operator-sdk-guide
A repo-specific map of how this operator is built with the operator-sdk /
kubebuilder (go.kubebuilder.io/v4) framework. It is not a general SDK tutorial -
it describes this operator so you can navigate and extend it safely.
Project shape
- Layout:
go.kubebuilder.io/v4, domain ibm.com, group operator (see PROJECT).
- Module:
github.com/IBM/ibm-licensing-operator, go 1.26.5.
- Entry point:
main.go builds the runtime scheme, creates a controller-runtime
Manager, and wires up the controllers.
Custom Resources (owned CRDs)
| Kind |
Package |
Version |
Scope |
Types file |
| IBMLicensing |
api/v1alpha1 |
v1alpha1 |
cluster-scoped |
ibmlicensing_types.go |
| IBMLicensingMetadata |
api/v1alpha1 |
v1alpha1 |
namespaced |
ibmlicensingmetadata_types.go |
| IBMLicensingDefinition |
api/v1 |
v1 |
namespaced |
ibmlicensingdefinition_types.go |
| IBMLicensingQuerySource |
api/v1 |
v1 |
namespaced |
ibmlicensingquerysource_types.go |
IBMLicensing is the primary CR: it defines a License Service instance (the operand).
OperandRequest (from ODLM) is watched but external - the operator reconciles it but
does not own its CRD (see PROJECT external: true).
Feature/config sub-types live in api/v1alpha1/features/ (auth, alerting, hyper-threading,
prometheus query source) and helpers in api/v1alpha1/{helper,license,features}.go.
Controllers (controllers/)
ibmlicensing_controller.go - the main reconciler. SetupWithManager does
For(&IBMLicensing{}) and Owns(...) on the resources it manages: Deployment,
Service, and (conditionally, when the APIs are present) Gateway API Gateway,
HTTPRoute, BackendTLSPolicy. Reconcile drives many sub-reconcilers - secrets/tokens,
ConfigMaps, Services, ServiceMonitors (RHMP + alerting), NetworkPolicy, Deployment,
certificate secrets - and updates .status. It can also create a default instance
(CreateDefaultInstance).
operandrequest_controller.go + operandrequest_discovery.go - integrate with IBM
ODLM OperandRequests.
operatorgroup_cleaner.go - housekeeping for OperatorGroups.
resources/ - the builders that construct the operand's Kubernetes objects
(containers, deployments, envs, services, CRDs, namespace scopes, operand bind info).
This is where the actual desired-state objects are assembled.
- Tests:
*_controller_test.go + suite_test.go (envtest/Ginkgo - see [[unit-test]]).
Manager & scheme wiring (main.go)
init() registers every API group the operator touches into the runtime scheme:
client-go core, this operator's v1alpha1/v1, OpenShift route and serviceca,
Prometheus monitoring, networking, Red Hat Marketplace meterdefinition, ODLM, and
OperatorFramework. The manager honors WATCH_NAMESPACE (parsed via
res.GetWatchNamespaceAsList()) and OPERATOR_NAMESPACE. Each controller's
SetupWithManager is called to register it with the manager.
kubebuilder markers (where behavior comes from)
Markers in Go comments drive code generation - edit the marker, then regenerate
(see [[generate-manifests]]):
// +kubebuilder:rbac:... on the controller (e.g. lines around the Reconcile func in
ibmlicensing_controller.go) generate config/rbac/role.yaml. Many here are
namespace=ibm-licensing scoped plus a few cluster rules.
// +kubebuilder:object:root=true, subresource:status, printcolumn, and validation
markers in the *_types.go files drive the CRD schema and DeepCopy generation.
hack/boilerplate.go.txt is the header injected into generated files.
The generation chain
edit api/*_types.go or controller RBAC markers
│
make generate ──▶ api/*/zz_generated.deepcopy.go (controller-gen object)
make manifests ──▶ config/crd/bases/*, config/rbac/role.yaml, CSV base
make bundle ──▶ bundle/manifests/* (CSV+CRDs), bundle/metadata/*
make bundle additionally uses operator-sdk generate kustomize manifests + kustomize
operator-sdk generate bundle, then post-processes with yq: it forces IBMLicensing to
be the first owned CRD in the CSV, folds RBAC and config/samples CRs into the CSV
(alm-examples), and injects common/relatedImages.yaml. Always regenerate and commit
these together with API changes.
Where to make common changes
| Goal |
Edit |
Then run |
| Add a spec field |
the relevant api/**/…_types.go |
make generate manifests |
| Change validation / printer columns |
kubebuilder markers in _types.go |
make manifests |
| Grant the operator a new permission |
+kubebuilder:rbac marker in the controller |
make manifests |
| Change operand objects (Deployment/Service/…) |
controllers/resources/*.go |
make unit-test |
| Add reconcile behavior |
ibmlicensing_controller.go (sub-reconcilers) |
make unit-test |
| New CRD version bump for the CSV |
version bump script + make bundle |
see [[contributing]] |
Related skills
- [[generate-manifests]] - run the generators after editing markers/types.
- [[unit-test]] - the envtest suites for the controllers described here.
- [[build-and-deploy]] - run the operator against a cluster.
- [[contributing]] - commit the generated artifacts with your change.
1---2name: operator-sdk-guide3description: Explains how operator-sdk and kubebuilder work specifically for the ibm-licensing-operator - its CRDs, controllers, reconcile flow, scheme/manager wiring, kubebuilder markers, and the code-generation chain. Use to understand the operator's architecture, where to add a field or controller behavior, what a kubebuilder marker does here, or how the CRD/RBAC/bundle artifacts are generated.4---56# operator-sdk-guide78A repo-specific map of how this operator is built with the operator-sdk /9kubebuilder (`go.kubebuilder.io/v4`) framework. It is not a general SDK tutorial -10it describes *this* operator so you can navigate and extend it safely.1112## Project shape1314- Layout: `go.kubebuilder.io/v4`, domain `ibm.com`, group `operator` (see `PROJECT`).15- Module: `github.com/IBM/ibm-licensing-operator`, go 1.26.5.16- Entry point: `main.go` builds the runtime `scheme`, creates a controller-runtime17 `Manager`, and wires up the controllers.1819## Custom Resources (owned CRDs)2021| Kind | Package | Version | Scope | Types file |22|------|---------|---------|-------|-----------|23| IBMLicensing | `api/v1alpha1` | v1alpha1 | **cluster-scoped** | `ibmlicensing_types.go` |24| IBMLicensingMetadata | `api/v1alpha1` | v1alpha1 | namespaced | `ibmlicensingmetadata_types.go` |25| IBMLicensingDefinition | `api/v1` | v1 | namespaced | `ibmlicensingdefinition_types.go` |26| IBMLicensingQuerySource | `api/v1` | v1 | namespaced | `ibmlicensingquerysource_types.go` |2728`IBMLicensing` is the primary CR: it defines a License Service instance (the operand).29`OperandRequest` (from ODLM) is watched but **external** - the operator reconciles it but30does not own its CRD (see `PROJECT` `external: true`).3132Feature/config sub-types live in `api/v1alpha1/features/` (auth, alerting, hyper-threading,33prometheus query source) and helpers in `api/v1alpha1/{helper,license,features}.go`.3435## Controllers (`controllers/`)3637- **`ibmlicensing_controller.go`** - the main reconciler. `SetupWithManager` does38 `For(&IBMLicensing{})` and `Owns(...)` on the resources it manages: Deployment,39 Service, and (conditionally, when the APIs are present) Gateway API `Gateway`,40 `HTTPRoute`, `BackendTLSPolicy`. `Reconcile` drives many sub-reconcilers - secrets/tokens,41 ConfigMaps, Services, ServiceMonitors (RHMP + alerting), NetworkPolicy, Deployment,42 certificate secrets - and updates `.status`. It can also create a default instance43 (`CreateDefaultInstance`).44- **`operandrequest_controller.go`** + `operandrequest_discovery.go` - integrate with IBM45 ODLM OperandRequests.46- **`operatorgroup_cleaner.go`** - housekeeping for OperatorGroups.47- **`resources/`** - the builders that construct the operand's Kubernetes objects48 (containers, deployments, envs, services, CRDs, namespace scopes, operand bind info).49 This is where the actual desired-state objects are assembled.50- Tests: `*_controller_test.go` + `suite_test.go` (envtest/Ginkgo - see [[unit-test]]).5152## Manager & scheme wiring (`main.go`)5354`init()` registers every API group the operator touches into the runtime `scheme`:55client-go core, this operator's `v1alpha1`/`v1`, OpenShift `route` and `serviceca`,56Prometheus `monitoring`, `networking`, Red Hat Marketplace `meterdefinition`, ODLM, and57OperatorFramework. The manager honors `WATCH_NAMESPACE` (parsed via58`res.GetWatchNamespaceAsList()`) and `OPERATOR_NAMESPACE`. Each controller's59`SetupWithManager` is called to register it with the manager.6061## kubebuilder markers (where behavior comes from)6263Markers in Go comments drive code generation - edit the marker, then regenerate64(see [[generate-manifests]]):6566- `// +kubebuilder:rbac:...` on the controller (e.g. lines around the `Reconcile` func in67 `ibmlicensing_controller.go`) generate `config/rbac/role.yaml`. Many here are68 `namespace=ibm-licensing` scoped plus a few cluster rules.69- `// +kubebuilder:object:root=true`, `subresource:status`, `printcolumn`, and validation70 markers in the `*_types.go` files drive the CRD schema and DeepCopy generation.71- `hack/boilerplate.go.txt` is the header injected into generated files.7273## The generation chain7475```text76edit api/*_types.go or controller RBAC markers77 │78 make generate ──▶ api/*/zz_generated.deepcopy.go (controller-gen object)79 make manifests ──▶ config/crd/bases/*, config/rbac/role.yaml, CSV base80 make bundle ──▶ bundle/manifests/* (CSV+CRDs), bundle/metadata/*81```8283`make bundle` additionally uses `operator-sdk generate kustomize manifests` + `kustomize`84+ `operator-sdk generate bundle`, then post-processes with `yq`: it forces IBMLicensing to85be the first owned CRD in the CSV, folds RBAC and `config/samples` CRs into the CSV86(`alm-examples`), and injects `common/relatedImages.yaml`. Always regenerate and commit87these together with API changes.8889## Where to make common changes9091| Goal | Edit | Then run |92|------|------|----------|93| Add a spec field | the relevant `api/**/…_types.go` | `make generate manifests` |94| Change validation / printer columns | kubebuilder markers in `_types.go` | `make manifests` |95| Grant the operator a new permission | `+kubebuilder:rbac` marker in the controller | `make manifests` |96| Change operand objects (Deployment/Service/…) | `controllers/resources/*.go` | `make unit-test` |97| Add reconcile behavior | `ibmlicensing_controller.go` (sub-reconcilers) | `make unit-test` |98| New CRD version bump for the CSV | version bump script + `make bundle` | see [[contributing]] |99100## Related skills101102- [[generate-manifests]] - run the generators after editing markers/types.103- [[unit-test]] - the envtest suites for the controllers described here.104- [[build-and-deploy]] - run the operator against a cluster.105- [[contributing]] - commit the generated artifacts with your change.