Skip to main content

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:

FunctionExamplePurpose
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 sourceOutputUsed by
internal/provision/openstack/templates/main.tf, provider.tf, variables.tfOpenTofu/Terraform
internal/provision/vmware/templates/main.tf, provider.tfOpenTofu/Terraform
internal/ansible/templates/inventory.yaml, group_vars/Kubespray
internal/gitops/templates/FluxCD Kustomizations, GitRepository sourcesFluxCD
internal/sops/templates/.sops.yamlSOPS 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.