Controller Template Variables

July 2, 2026 · View on GitHub

This document describes the template variables consumed by the controller setup template at pkg/pipeline/templates/controller.go.tmpl. These variables are populated by ControllerGenerator.Generate for each managed resource and are used to render the per-resource zz_controller.go file.

The controller template can be overridden via config.Provider (see WithControllerTemplate in pkg/config/provider.go). Any custom template MUST honor the contract described below.

Warning

Overriding the controller template is an advanced feature.

  • Use this capability with great care, and only when the default template genuinely cannot accommodate your provider's requirements. If at all possible, prefer to stay on the default template and contribute upstream changes that benefit all providers.
  • The set of template variables and their semantics are not covered by the same compatibility guarantees as the rest of the public Go API. Upjet may add, remove, rename, or change the meaning of template variables without a deprecation cycle. Such changes will land in a new minor release and be called out in the corresponding release notes.
  • Custom templates that diverge from the default may break on any minor upgrade. Plan for the maintenance cost of keeping a fork of this template in sync.

Template Variables

The following keys are available inside the template via {{ .<Name> }}.

VariableTypeDescription
HeaderstringLicense header rendered from hack/boilerplate.go.txt.
GenStatementstring"Code generated by upjet. DO NOT EDIT." style comment block.
ImportsstringThe resolved import block for the generated file.
PackagestringGo package name for the generated controller (lower-cased kind). Used in the package clause.
CRD.KindstringKubernetes Kind of the managed resource. Used to reference the generated *_GroupVersionKind and list types (e.g. {{ .CRD.Kind }}_GroupVersionKind, {{ .CRD.Kind }}List).
DisableNameInitializerboolWhen false, the template appends managed.NewNameAsExternalName to the initializer chain. Set to true for resources whose external name is not derived from metadata.name.
TypePackageAliasstringImport alias (with trailing .) for the generated API types package. Used to qualify Kind references, e.g. {{ .TypePackageAlias }}{{ .CRD.Kind }}{}.
UseAsyncboolSelects the asynchronous connector variants and wires APICallbacks. Required for long-running Create/Update/Delete operations.
UseTerraformPluginSDKClientboolSelects the no-fork Terraform Plugin SDK v2 connector (NewTerraformPluginSDK[Async]Connector). Mutually exclusive with the framework client.
UseTerraformPluginFrameworkClientboolSelects the no-fork Terraform Plugin Framework connector (NewTerraformPluginFramework[Async]Connector). Mutually exclusive with the SDK client. When both UseTerraformPlugin*Client flags are false, the template falls back to the CLI-based NewConnector.
ResourceTypestringTerraform resource type name (e.g. aws_vpc). Used as the key into o.Provider.Resources[...].
Initializers[]config.NewInitializerFnWhen non-empty, the template emits a loop that appends provider-supplied initializers to the chain. Only the truthiness (non-empty slice) is consumed inside the template.
FeaturesPackageAliasstringImport alias for the provider's features package. Only set when the provider exposes a features package. When unset, the template skips all EnableBetaManagementPolicies wiring, preserving compatibility with providers that have no features package.

Connector Selection Matrix

The template chooses the external connector based on the combination of UseTerraformPluginSDKClient, UseTerraformPluginFrameworkClient, and UseAsync:

SDKFrameworkAsyncConnector
truefalsefalsetjcontroller.NewTerraformPluginSDKConnector
truefalsetruetjcontroller.NewTerraformPluginSDKAsyncConnector
falsetruefalsetjcontroller.NewTerraformPluginFrameworkConnector
falsetruetruetjcontroller.NewTerraformPluginFrameworkAsyncConnector
falsefalseanytjcontroller.NewConnector (CLI / fork-based)

UseTerraformPluginSDKClient and UseTerraformPluginFrameworkClient must not both be true for the same resource.

Generated Entry Points

For every resource the rendered file exposes two functions:

  • Setup(mgr ctrl.Manager, o tjcontroller.Options) error — registers the reconciler eagerly.
  • SetupGated(mgr ctrl.Manager, o tjcontroller.Options) error — defers reconciler registration until the CRD's GVK is observed via o.Options.Gate.Register.

Both functions assume that o.Provider.Resources["{{ .ResourceType }}"] is populated at runtime, regardless of which connector is selected.

Generated Output

For every resource the rendered file is written under the controllers directory the pipeline is generating into:

internal/controller/[<scope>/]<group>/<kind>/zz_controller.go
  • <scope> is present only when the provider generates both cluster-scoped and namespaced managed resources (Upjet v2 namespaced resources). In that case the pipeline runs once per scope and writes into internal/controller/cluster/... and internal/controller/namespaced/... respectively. A provider that generates only cluster-scoped resources omits this segment and writes directly under internal/controller/.
  • <group> is the short group name — the first dot-separated segment of the API group, lower-cased (e.g. ec2 for ec2.aws.upbound.io).
  • <kind> is the lower-cased resource kind.

ControllerGenerator itself only joins <ctrlDir>/<group>/<kind>; the <scope> prefix (when present) comes from the ctrlDir that the pipeline runner (pipeline.Run) selects for each scope.

Overriding the Template

To supply a custom controller template (for example, when extending the generated Setup function), pass config.WithControllerTemplate through config.Provider. The custom template must accept all variables documented above; otherwise the rendered file will not compile against the upjet runtime contracts.

When Template Errors Surface

Errors in a custom controller template are reported at two distinct stages:

  • Provider generation time. Static template errors — such as a template string that fails to parse (text/template.Parse() failures), malformed actions, or references to undefined fields evaluated during execution — are raised by the code generation pipeline and cause provider generation to fail.
  • Provider build / lint time. Syntactically valid templates that produce invalid Go (for example, an empty template that emits no package clause, or output that omits imports required by the upjet runtime contracts) parse cleanly but fail later when the generated zz_controller.go files are compiled or linted as part of the provider build.