Template System Architecture
OpenPrime uses a custom template processing system powered by Injecto to generate infrastructure code.
Overviewβ
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β Templates β β Configuration β β Injecto β
β (.tf.tpl) ββββββΆβ (JSON) ββββββΆβ Processor β
βββββββββββββββββββ βββββββββββββββββββ ββββββββββ¬βββββββββ
β
βΌ
βββββββββββββββββββ
β Generated Code β
β (.tf files) β
βββββββββββββββββββ
Template Syntaxβ
Parameters (@param)β
Define configurable values:
# @param cluster_name required
# @param cluster_version default=1.28
# @param node_count default=3 type=number
resource "aws_eks_cluster" "main" {
name = "${cluster_name}"
version = "${cluster_version}"
}
Parameter Options:
| Option | Description | Example |
|---|---|---|
required | Must be provided | # @param name required |
default | Default value | # @param count default=3 |
type | Value type | # @param enabled type=boolean |
Conditional Sections (@section)β
Include/exclude code blocks:
# @section enable_logging begin
resource "aws_cloudwatch_log_group" "cluster" {
name = "/aws/eks/${cluster_name}/cluster"
retention_in_days = 30
}
# @section enable_logging end
Sections are included when the condition evaluates to truthy.
Loops (@foreach)β
Iterate over collections:
# @foreach node_group in node_groups begin
resource "aws_eks_node_group" "${node_group.name}" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "${node_group.name}"
scaling_config {
desired_size = ${node_group.desired_size}
min_size = ${node_group.min_size}
max_size = ${node_group.max_size}
}
instance_types = ["${node_group.instance_type}"]
# @section node_group.labels begin
labels = ${jsonencode(node_group.labels)}
# @section end
}
# @foreach end
Conditionals (@if)β
Inline conditional expressions:
resource "aws_eks_cluster" "main" {
name = var.cluster_name
version = var.cluster_version
# @if public_access begin
vpc_config {
endpoint_public_access = true
}
# @if end
# @if private_access begin
vpc_config {
endpoint_public_access = false
endpoint_private_access = true
}
# @if end
}
Injecto Serviceβ
API Endpointsβ
Process Templatesβ
POST /process-upload
Content-Type: multipart/form-data
template_file: <template.tf.tpl>
config: {"cluster_name": "my-cluster", ...}
Response:
{
"success": true,
"files": {
"main.tf": "...",
"variables.tf": "...",
"outputs.tf": "..."
}
}
Health Checkβ
GET /health
Response:
{
"status": "healthy",
"version": "1.0.0"
}
Processing Pipelineβ
def process_template(template: str, config: dict) -> str:
# 1. Parse parameters
params = extract_params(template)
validate_params(params, config)
# 2. Process sections
template = process_sections(template, config)
# 3. Process loops
template = process_loops(template, config)
# 4. Substitute variables
template = substitute_variables(template, config)
# 5. Clean up directives
template = cleanup_directives(template)
return template
Template Organizationβ
Directory Structureβ
openprime-infra-templates/
βββ templates/
β βββ aws/
β β βββ eks/
β β β βββ main.tf.tpl
β β β βββ variables.tf.tpl
β β β βββ outputs.tf.tpl
β β β βββ iam.tf.tpl
β β βββ rds/
β β β βββ main.tf.tpl
β β β βββ security-group.tf.tpl
β β βββ s3/
β β βββ main.tf.tpl
β βββ azure/
β β βββ aks/
β βββ gcp/
β βββ gke/
βββ shared/
β βββ backend.tf.tpl
β βββ versions.tf.tpl
βββ local/
βββ terraforge.sh
βββ extract_parameters.sh
Shared Templatesβ
Common code reused across providers:
# shared/backend.tf.tpl
# @param backend_bucket required
# @param backend_key required
# @param backend_region default=us-east-1
terraform {
backend "s3" {
bucket = "${backend_bucket}"
key = "${backend_key}"
region = "${backend_region}"
dynamodb_table = "terraform-state-lock"
encrypt = true
}
}
Configuration Mappingβ
Service Schema β Template Configβ
// servicesConfig.js
kubernetes: {
template: 'aws/eks',
schema: {
clusterName: { type: 'string', param: 'cluster_name' },
version: { type: 'select', param: 'cluster_version' },
nodeGroups: { type: 'array', param: 'node_groups' }
}
}
Environment β Injecto Inputβ
// Backend transforms environment to template config
function buildTemplateConfig(environment) {
const config = {
environment_name: environment.name,
provider: environment.provider,
region: environment.region,
...environment.configuration
};
// Map services to template params
for (const [service, settings] of Object.entries(environment.services)) {
if (settings.enabled) {
const schema = servicesConfig[service].schema;
for (const [key, value] of Object.entries(settings)) {
const param = schema[key]?.param || key;
config[param] = value;
}
}
}
return config;
}
Generated Outputβ
Example: EKS Clusterβ
Input Configuration:
{
"cluster_name": "production",
"cluster_version": "1.28",
"node_groups": [
{
"name": "general",
"instance_type": "t3.large",
"desired_size": 5,
"min_size": 2,
"max_size": 10
}
],
"enable_logging": true
}
Generated main.tf:
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 19.0"
cluster_name = "production"
cluster_version = "1.28"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
eks_managed_node_groups = {
general = {
instance_types = ["t3.large"]
min_size = 2
max_size = 10
desired_size = 5
}
}
cluster_enabled_log_types = [
"api",
"audit",
"authenticator",
"controllerManager",
"scheduler"
]
}
resource "aws_cloudwatch_log_group" "cluster" {
name = "/aws/eks/production/cluster"
retention_in_days = 30
}
Testing Templatesβ
Local Testingβ
cd openprime-infra-templates/local
# Process templates
./terraforge.sh
# Validate generated code
cd output/
terraform init
terraform validate
terraform plan
Unit Testingβ
# test_templates.py
def test_eks_template_basic():
template = load_template('aws/eks/main.tf.tpl')
config = {
'cluster_name': 'test',
'cluster_version': '1.28',
'node_groups': []
}
result = process_template(template, config)
assert 'cluster_name = "test"' in result
assert 'cluster_version = "1.28"' in result
Best Practicesβ
- Keep templates DRY - Extract common patterns to shared templates
- Use modules - Reference Terraform Registry modules
- Pin versions - Lock provider and module versions
- Document parameters - Add descriptions to all
@paramdirectives - Validate early - Catch errors before code generation
- Test thoroughly - Ensure generated code compiles and plans cleanly