This document describes how to add a new direct controller, and how to configure controller routing.
A top-level parent controller routes reconciliation to one of three underlying controllers: Terraform (TF), DCL, or Direct. The controller is selected using the following order of precedence:
-
Resource Annotation (deprecated): A resource can specify a controller directly using the annotation
cnrm.cloud.google.com/reconciler: direct. This is supported for backward compatibility, but its use is discouraged and it will be deprecated in the future. -
ConfigConnectorContext Override: The
ConfigConnectorContextresource allows for overriding the controller for a specific resourceGroupKindusing thespec.experiments.controllerOverridesfield. -
Static Configuration: A static map in
pkg/controller/resourceconfig/static_config.godefines the default and supported controllers for each resource. This is the default mechanism if no overrides are specified.
You can use a script to update the static configuration, which will be covered in the scenarios below.
Run the following command to generate a controller template:
cd dev/tools/controllerbuilder
go run main.go generate-controller --service <YOUR_SERVICE> --api-version <VERSION> --resource <YOUR_RESOURCE>:<PROTO_RESOURCE>
Fix the generated code to make your controller run.
The controller template has implemented the model interfacefind, create, update, deleteand export. You may need to update the code to fit your resource.
Most Config Connector resources need references like spec.projectRef. You should add those references in AdapterForObject using functions Resolve<RefResource>.
If there is no previous reference method, you may need to add a new Resolve<RefResource>.
Check the validation guide to make sure your validation is complete.
This section covers how to configure the controller routing for your resource and verify it. The static controller configuration is generated by a script that inspects your CRD definitions and controller code.
There are three main scenarios:
- Adding a new direct resource at alpha.
- Migrating an existing TF/DCL-based resource to direct.
- Promoting an alpha direct resource to beta.
The general workflow for updating the controller configuration is:
- Ensure your CRD file (
config/crds/resources/<crd_file>.yaml) is up-to-date. For migrated resources, this includes ensuring thecnrm.cloud.google.com/tf2crdorcnrm.cloud.google.com/dcl2crdlabels are set correctly. - Ensure your direct controller implementation calls
RegisterModelfor your resource's GVK. - Run the generation script:
This will automatically update
./dev/tasks/generate_static_config.py
pkg/controller/resourceconfig/static_config.go.
For a new resource that will only have a direct controller, the script will automatically configure it as the default and only controller, as long as the CRD does not have tf2crd or dcl2crd labels.
-
Generate the static controller configuration: After creating your CRD and direct controller, run the script.
./dev/tasks/generate_static_config.py
-
Verify the controller: Run the MockGCP tests. Since
directis the default, no special environment variables are needed.hack/compare-mock fixtures/<your_resource_test>
When migrating an existing resource, you'll want to support both the old (TF/DCL) and new (direct) controllers. The old controller will remain the default. The script handles this automatically if the tf2crd or dcl2crd label is present on the CRD.
-
Generate the static controller configuration: After adding your direct controller with
RegisterModel, run the script. It will detect the existingtf2crdordcl2crdlabel on the CRD and adddirectas a supported controller, while keeping the original as the default../dev/tasks/generate_static_config.py
-
Verify the direct controller: To test your new direct controller, you need to override the default routing. You can do this in two ways:
-
Using an environment variable (for testing): Set the
KCC_USE_DIRECT_RECONCILERSenvironment variable to force the use of the direct controller for your resource kind.KCC_USE_DIRECT_RECONCILERS=<YOUR_KIND> hack/compare-mock fixtures/<your_resource_test>
Note: Differences in http.logs such as
user-agentand method url are expected. Please regenerate test logs beforecompare-mock. -
Using a
ConfigConnectorContextoverride (for cluster-wide testing): You can create aConfigConnectorContextobject in your test namespace to override the controller for your resource.apiVersion: core.cnrm.cloud.google.com/v1beta1 kind: ConfigConnectorContext metadata: name: configconnectorcontext.core.cnrm.cloud.google.com namespace: ${NAMESPACE} spec: experiments: controllerOverrides: - group: <GROUP> kind: <KIND> controller: direct
-
When you promote a resource from alpha to beta, you will typically make the direct controller the default for the new beta version.
-
Update the CRD for the beta version: When you create the
v1beta1CRD, ensure it does not have thecnrm.cloud.google.com/tf2crdorcnrm.cloud.google.com/dcl2crdlabels. This will signal to the generation script thatdirectshould be the default. -
Generate the static controller configuration: Run the script. It will generate the configuration for the new
v1beta1version withdirectas the default controller../dev/tasks/generate_static_config.py
-
Verify the controller: Update your test fixtures to use
apiVersion: <GROUP>/v1beta1. Then run the MockGCP tests.hack/compare-mock fixtures/<your_resource_test>
- The PRs shall pass the MockGCP tests.
- For Beta resource, the roundtrip fuzz tests shall cover all the fields in
specandstatus.observedStatefields. Example