Template Rendering Internals
Purpose: For contributors, explains how Go templates transform config into generated artifacts.
What template rendering does
The CLI generates infrastructure-as-code and GitOps manifests from a single cluster configuration YAML file. Template rendering is the mechanism that transforms the configuration struct into concrete files: Terraform main.tf, Kubespray inventory.yaml, FluxCD Kustomizations, and SOPS encryption configs.
How it works
The template engine lives in internal/template/. It uses Go's text/template package extended with Sprig functions (via Masterminds/sprig/v3).
The rendering flow:
ClusterConfig struct
↓ (passed as template data context)
Go template file (.tmpl)
↓ (rendered by internal/template/)
Output file (main.tf, inventory.yaml, etc.)
Each provider package (internal/provision/<provider>/) contains its own templates/ directory with provider-specific templates.
Template data context
Templates receive the full ClusterConfig struct. You access fields using Go template syntax:
{{ .Opencenter.Cluster.ClusterName }}
{{ .Opencenter.Infrastructure.Cloud.Openstack.AuthURL }}
{{ range .Opencenter.Services }}
- {{ .Name }}
{{ end }}
Sprig functions
Sprig adds ~70 utility functions to Go templates. Commonly used ones in the CLI:
| Function | Example | Purpose |
|---|---|---|
default | {{ default "us-east-1" .Region }} | Provide fallback values |
quote | {{ .Name | quote }} | Wrap in double quotes |
indent | {{ .Block | indent 4 }} | Indent multi-line strings |
toYaml | {{ .Values | toYaml }} | Serialize to YAML |
contains | {{ if contains "openstack" .Provider }} | String matching |
ternary | {{ ternary "yes" "no" .Enabled }} | Conditional values |
Template sandbox
The internal/template/ package includes a sandbox that restricts template execution. Templates cannot:
- Execute shell commands
- Access the filesystem outside the output directory
- Import arbitrary Go packages
This prevents template injection attacks if untrusted data enters the config.
Where templates produce output
| Template source | Output | Used by |
|---|---|---|
internal/provision/openstack/templates/ | main.tf, provider.tf, variables.tf | OpenTofu/Terraform |
internal/provision/vmware/templates/ | main.tf, provider.tf | OpenTofu/Terraform |
internal/ansible/templates/ | inventory.yaml, group_vars/ | Kubespray |
internal/gitops/templates/ | FluxCD Kustomizations, GitRepository sources | FluxCD |
internal/sops/templates/ | .sops.yaml | SOPS encryption |
Testing templates
Use opencenter cluster render to render templates without applying them:
./bin/opencenter cluster render my-cluster
This writes rendered output to stdout or a specified directory, letting you inspect the generated files before running setup.
Template-specific tests live alongside the templates in cmd/cluster_template_test.go and cmd/cluster_render_integration_test.go.
Common misconceptions
- Templates don't execute Terraform or Ansible. They only generate the files. Execution happens in later pipeline stages.
- Sprig functions are available in all templates, not just Terraform ones. The same engine renders Kubespray inventories and FluxCD manifests.
- The config struct is the only data source for templates. Templates don't read files or make API calls during rendering.