362 lines
9.6 KiB
Markdown
362 lines
9.6 KiB
Markdown
# Templates
|
|
|
|
Jinja2 and Nickel templates for configuration and deployment generation.
|
|
|
|
## Purpose
|
|
|
|
Templates provide:
|
|
- **Nickel output generation** - Jinja2 templates for TypeDialog nickel-roundtrip
|
|
- **Docker Compose generation** - Infrastructure-as-code for containerized deployment
|
|
- **Kubernetes manifests** - Declarative deployment manifests
|
|
- **TOML export** - Service configuration generation for Rust codebase
|
|
|
|
## File Organization
|
|
|
|
```plaintext
|
|
templates/
|
|
├── README.md # This file
|
|
├── orchestrator-config.ncl.j2 # Nickel output template (Jinja2)
|
|
├── control-center-config.ncl.j2 # Nickel output template (Jinja2)
|
|
├── mcp-server-config.ncl.j2 # Nickel output template (Jinja2)
|
|
├── installer-config.ncl.j2 # Nickel output template (Jinja2)
|
|
├── docker-compose/ # Docker Compose templates
|
|
│ ├── platform-stack.solo.yml.ncl
|
|
│ ├── platform-stack.multiuser.yml.ncl
|
|
│ ├── platform-stack.cicd.yml.ncl
|
|
│ └── platform-stack.enterprise.yml.ncl
|
|
├── kubernetes/ # Kubernetes templates
|
|
│ ├── orchestrator-deployment.yaml.ncl
|
|
│ ├── orchestrator-service.yaml.ncl
|
|
│ ├── control-center-deployment.yaml.ncl
|
|
│ ├── control-center-service.yaml.ncl
|
|
│ └── platform-ingress.yaml.ncl
|
|
└── configs/ # Service config templates (optional)
|
|
├── orchestrator-config.toml.ncl
|
|
├── control-center-config.toml.ncl
|
|
└── mcp-server-config.toml.ncl
|
|
```
|
|
|
|
## Jinja2 Config Templates
|
|
|
|
**Critical for TypeDialog nickel-roundtrip workflow**:
|
|
|
|
```bash
|
|
typedialog-web nickel-roundtrip "$CONFIG" "forms/{service}-form.toml" --output "$CONFIG" --template "templates/{service}-config.ncl.j2"
|
|
```
|
|
|
|
### Template Pattern: orchestrator-config.ncl.j2
|
|
|
|
```nickel
|
|
# Orchestrator Configuration - Nickel Format
|
|
# Auto-generated by provisioning TypeDialog
|
|
# Edit via: nu scripts/configure.nu orchestrator {mode}
|
|
|
|
{
|
|
orchestrator = {
|
|
# Workspace Configuration
|
|
workspace = {
|
|
{%- if workspace_name %}
|
|
name = "{{ workspace_name }}",
|
|
{%- endif %}
|
|
{%- if workspace_path %}
|
|
path = "{{ workspace_path }}",
|
|
{%- endif %}
|
|
{%- if workspace_enabled is defined %}
|
|
enabled = {{ workspace_enabled | lower }},
|
|
{%- endif %}
|
|
{%- if multi_workspace is defined %}
|
|
multi_workspace = {{ multi_workspace | lower }},
|
|
{%- endif %}
|
|
},
|
|
|
|
# Server Configuration
|
|
server = {
|
|
{%- if server_host %}
|
|
host = "{{ server_host }}",
|
|
{%- endif %}
|
|
{%- if server_port %}
|
|
port = {{ server_port }},
|
|
{%- endif %}
|
|
{%- if server_workers %}
|
|
workers = {{ server_workers }},
|
|
{%- endif %}
|
|
{%- if server_keep_alive %}
|
|
keep_alive = {{ server_keep_alive }},
|
|
{%- endif %}
|
|
},
|
|
|
|
# Storage Configuration
|
|
storage = {
|
|
{%- if storage_backend %}
|
|
backend = '{{ storage_backend }},
|
|
{%- endif %}
|
|
{%- if storage_path %}
|
|
path = "{{ storage_path }}",
|
|
{%- endif %}
|
|
{%- if surrealdb_url %}
|
|
surrealdb_url = "{{ surrealdb_url }}",
|
|
{%- endif %}
|
|
},
|
|
|
|
# Queue Configuration
|
|
queue = {
|
|
{%- if max_concurrent_tasks %}
|
|
max_concurrent_tasks = {{ max_concurrent_tasks }},
|
|
{%- endif %}
|
|
{%- if retry_attempts %}
|
|
retry_attempts = {{ retry_attempts }},
|
|
{%- endif %}
|
|
{%- if retry_delay %}
|
|
retry_delay = {{ retry_delay }},
|
|
{%- endif %}
|
|
{%- if task_timeout %}
|
|
task_timeout = {{ task_timeout }},
|
|
{%- endif %}
|
|
},
|
|
|
|
# Monitoring Configuration (optional)
|
|
{%- if enable_monitoring is defined and enable_monitoring %}
|
|
monitoring = {
|
|
enabled = true,
|
|
{%- if metrics_interval %}
|
|
metrics_interval = {{ metrics_interval }},
|
|
{%- endif %}
|
|
{%- if health_check_interval %}
|
|
health_check_interval = {{ health_check_interval }},
|
|
{%- endif %}
|
|
},
|
|
{%- endif %}
|
|
},
|
|
}
|
|
```
|
|
|
|
### Key Jinja2 Patterns
|
|
|
|
**Conditional blocks** (only include if field is set):
|
|
|
|
```nickel
|
|
{%- if workspace_name %}
|
|
name = "{{ workspace_name }}",
|
|
{%- endif %}
|
|
```
|
|
|
|
**String values** (with quotes):
|
|
|
|
```nickel
|
|
{%- if storage_backend %}
|
|
backend = '{{ storage_backend }}, # Enum (atom syntax)
|
|
{%- endif %}
|
|
```
|
|
|
|
**Numeric values** (no quotes):
|
|
|
|
```nickel
|
|
{%- if server_port %}
|
|
port = {{ server_port }}, # Number
|
|
{%- endif %}
|
|
```
|
|
|
|
**Boolean values** (lower case):
|
|
|
|
```nickel
|
|
{%- if workspace_enabled is defined %}
|
|
enabled = {{ workspace_enabled | lower }}, # Boolean (true/false)
|
|
{%- endif %}
|
|
```
|
|
|
|
**Comments** (for generated files):
|
|
|
|
```nickel
|
|
# Auto-generated by provisioning TypeDialog
|
|
# Edit via: nu scripts/configure.nu orchestrator {mode}
|
|
```
|
|
|
|
## Docker Compose Templates
|
|
|
|
Nickel templates that import from `values/*.ncl`:
|
|
|
|
```nickel
|
|
# templates/docker-compose/platform-stack.solo.yml.ncl
|
|
# Docker Compose Platform Stack - Solo Mode
|
|
# Imports config from values/orchestrator.solo.ncl
|
|
|
|
let orchestrator_config = import "../../values/orchestrator.solo.ncl" in
|
|
let control_center_config = import "../../values/control-center.solo.ncl" in
|
|
|
|
{
|
|
version = "3.8",
|
|
services = {
|
|
orchestrator = {
|
|
image = "provisioning-orchestrator:latest",
|
|
container_name = "orchestrator",
|
|
ports = [
|
|
"%{std.to_string orchestrator_config.orchestrator.server.port}:9090",
|
|
],
|
|
environment = {
|
|
ORCHESTRATOR_SERVER_HOST = orchestrator_config.orchestrator.server.host,
|
|
ORCHESTRATOR_SERVER_PORT = std.to_string orchestrator_config.orchestrator.server.port,
|
|
ORCHESTRATOR_STORAGE_BACKEND = orchestrator_config.orchestrator.storage.backend,
|
|
},
|
|
volumes = [
|
|
"./data/orchestrator:%{orchestrator_config.orchestrator.storage.path}",
|
|
],
|
|
restart = "unless-stopped",
|
|
},
|
|
control-center = {
|
|
image = "provisioning-control-center:latest",
|
|
container_name = "control-center",
|
|
ports = [
|
|
"%{std.to_string control_center_config.control_center.server.port}:8080",
|
|
],
|
|
environment = {
|
|
CONTROL_CENTER_SERVER_HOST = control_center_config.control_center.server.host,
|
|
CONTROL_CENTER_SERVER_PORT = std.to_string control_center_config.control_center.server.port,
|
|
},
|
|
restart = "unless-stopped",
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
### Rendering Docker Compose
|
|
|
|
```bash
|
|
# Export Nickel template to YAML
|
|
nickel export --format json templates/docker-compose/platform-stack.solo.yml.ncl | yq -P > docker-compose.solo.yml
|
|
```
|
|
|
|
## Kubernetes Templates
|
|
|
|
Nickel templates for Kubernetes manifests:
|
|
|
|
```nickel
|
|
# templates/kubernetes/orchestrator-deployment.yaml.ncl
|
|
let config = import "../../values/orchestrator.solo.ncl" in
|
|
|
|
{
|
|
apiVersion = "apps/v1",
|
|
kind = "Deployment",
|
|
metadata = {
|
|
name = "orchestrator",
|
|
labels = {
|
|
app = "orchestrator",
|
|
},
|
|
},
|
|
spec = {
|
|
replicas = 1,
|
|
selector = {
|
|
matchLabels = {
|
|
app = "orchestrator",
|
|
},
|
|
},
|
|
template = {
|
|
metadata = {
|
|
labels = {
|
|
app = "orchestrator",
|
|
},
|
|
},
|
|
spec = {
|
|
containers = [
|
|
{
|
|
name = "orchestrator",
|
|
image = "provisioning-orchestrator:latest",
|
|
ports = [
|
|
{
|
|
containerPort = 9090,
|
|
},
|
|
],
|
|
env = [
|
|
{
|
|
name = "ORCHESTRATOR_SERVER_PORT",
|
|
value = std.to_string config.orchestrator.server.port,
|
|
},
|
|
{
|
|
name = "ORCHESTRATOR_STORAGE_BACKEND",
|
|
value = config.orchestrator.storage.backend,
|
|
},
|
|
],
|
|
volumeMounts = [
|
|
{
|
|
name = "data",
|
|
mountPath = config.orchestrator.storage.path,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
volumes = [
|
|
{
|
|
name = "data",
|
|
persistentVolumeClaim = {
|
|
claimName = "orchestrator-pvc",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
## Rendering Templates
|
|
|
|
### Render to JSON
|
|
|
|
```bash
|
|
nickel export --format json templates/orchestrator-config.ncl.j2 > config.json
|
|
```
|
|
|
|
### Render to YAML (via yq)
|
|
|
|
```bash
|
|
nickel export --format json templates/kubernetes/orchestrator-deployment.yaml.ncl | yq -P > deployment.yaml
|
|
```
|
|
|
|
### Render to TOML
|
|
|
|
```bash
|
|
nickel export --format toml templates/configs/orchestrator-config.toml.ncl > config.toml
|
|
```
|
|
|
|
## Template Variables
|
|
|
|
Variables in templates come from:
|
|
1. **Form values** (TypeDialog input)
|
|
2. **Imported configs** (Nickel imports)
|
|
3. **Constraint interpolation** (constraints.toml)
|
|
|
|
## Best Practices
|
|
|
|
1. **Use conditional blocks** - Only include fields if set
|
|
2. **Import configs** - Reuse Nickel configs in templates
|
|
3. **Type conversion** - Use `std.to_string` for numeric values
|
|
4. **Comments** - Explain generated/auto-edited markers
|
|
5. **Validation** - Use `nickel typecheck` to verify templates
|
|
6. **Environment variables** - Prefer env over hardcoding
|
|
|
|
## Template Testing
|
|
|
|
```bash
|
|
# Typecheck Jinja2 + Nickel template
|
|
nickel typecheck templates/orchestrator-config.ncl.j2
|
|
|
|
# Evaluate and view output
|
|
nickel eval templates/orchestrator-config.ncl.j2
|
|
|
|
# Export and validate output
|
|
nickel export --format json templates/orchestrator-config.ncl.j2 | jq '.'
|
|
```
|
|
|
|
## Adding a New Template
|
|
|
|
1. **Create template file** (`{service}-config.ncl.j2` or `{name}.yml.ncl`)
|
|
2. **Define structure** (Nickel or Jinja2)
|
|
3. **Import configs** (if Nickel)
|
|
4. **Use variables** (from forms or imports)
|
|
5. **Typecheck**: `nickel typecheck templates/{file}`
|
|
6. **Test rendering**: `nickel export {format} templates/{file}`
|
|
|
|
---
|
|
|
|
**Version**: 1.0.0
|
|
**Last Updated**: 2025-01-05
|