2025-10-07 11:12:02 +01:00
# Configuration Templates
**Purpose**: Template files for generating workspace configurations
2025-12-11 21:50:42 +00:00
## Template Extension Conventions
This project uses **TWO template extensions** for different purposes:
### `.template` Extension (This Directory)
- **Purpose**: Workspace initialization only
- **Engine**: Simple string substitution (`{{variable}}` )
- **Usage**: One-time generation during workspace creation
- **Dependency**: None (no plugins required)
- **Complexity**: Low (no loops/conditionals needed)
**Example**:
```yaml
workspace:
name: "{{workspace.name}}"
created: "{{now.iso}}"
```
**When to use**:
- Workspace initialization templates
- One-time setup files
- No dynamic logic needed
### `.j2` Extension (Rest of Codebase)
- **Purpose**: Runtime configuration generation
- **Engine**: Jinja2 (via `nu_plugin_tera` )
- **Usage**: Dynamic config rendering during operations
- **Dependency**: Requires `nu_plugin_tera` plugin
- **Complexity**: High (conditionals, loops, filters)
**Example**:
```jinja2
{%- if taskserv.mode == "ha" %}
REPLICAS={{taskserv.replicas}}
{%- endif %}
{% for node in cluster.nodes -%}
NODE_{{loop.index}}={{node.hostname}}
{% endfor %}
```
**When to use**:
- Runtime configuration generation
- Dynamic values from environment
- Complex logic (conditionals, loops)
### Why Two Extensions?
1. **Separation of Concerns** : Init and runtime are fundamentally different operations
- Init happens once during workspace creation
- Runtime happens continuously during operations
2. **No Plugin Dependency for Init** : Workspace creation works without external plugins
- Simple string replacement is sufficient for initialization
- `nu_plugin_tera` is only needed for runtime rendering
- Initialization is more portable and reliable
3. **Semantic Clarity** : Extension signals the purpose immediately
- Developers see `.template` and know: "This is for initialization"
- Developers see `.j2` and know: "This is for runtime rendering"
- No ambiguity about usage context
4. **Appropriate Complexity** : Each extension matches its use case
- Init templates don't need loops/conditionals (simple substitution is enough)
- Runtime templates need full Jinja2 power (conditionals, loops, filters)
- Using the right tool for the job
### Codebase Statistics
**Template Distribution**:
- `.j2` templates: 134 files (88%) - Runtime generation
- `.template` templates: 16 files (10%) - Workspace initialization
- `.tera` templates: 3 files (2%) - Plugin examples
The two-tier system reflects the actual use case distribution in the codebase.
## KCL Module Structure
Workspaces use a **clear directory structure for KCL modules** :
```
workspace/
├── config/
│ ├── kcl.mod # Workspace package, imports provisioning from ../.kcl
│ └── provisioning.k # Workspace-specific config overrides (SST pattern)
├── .provisioning/ # Metadata only
│ └── metadata.yaml # Workspace metadata and version info
└── .kcl/ # Main KCL package "provisioning"
├── kcl.mod # Package definition
├── workspace_config.k # Schema definitions (SST)
├── workspace_config_defaults.k # Default values (SST)
├── batch.k
├── cluster.k
└── ... other KCL modules
```
### Directory Purposes
| Directory | Purpose | Contents |
|-----------|---------|----------|
| `.provisioning/` | **Metadata only** | `metadata.yaml` - workspace versioning and compatibility |
| `.kcl/` | **KCL modules** | All KCL configuration files and schemas |
| `config/` | **Workspace config** | Runtime configuration files generated from templates |
### SST Pattern (Single Source of Truth)
Workspace configuration follows the SST pattern:
1. **Schema** (`.kcl/workspace_config.k` ) - Type-safe schema definitions
2. **Defaults** (`.kcl/workspace_config_defaults.k` ) - Base default values
3. **Overrides** (`config/provisioning.k` ) - Workspace-specific customizations
**Never edit** `.provisioning/workspace_config.k` or `.provisioning/workspace_config_defaults.k` - they are copies only.
**Always edit** in `.kcl/` directory instead.
2025-10-07 11:12:02 +01:00
## Important
**These files are TEMPLATES ONLY. They are NEVER loaded at runtime.**
The provisioning system generates workspace configurations from these templates during workspace initialization. Once generated, the workspace uses its own `config/provisioning.yaml` and related configs.
## Available Templates
### 1. workspace-provisioning.yaml.template
Main workspace configuration template. Generates: `{workspace}/config/provisioning.yaml`
**Variables:**
- `{{workspace.name}}` - Workspace name
- `{{workspace.path}}` - Absolute workspace path
- `{{now.iso}}` - Timestamp
**Sections:**
- workspace - Workspace metadata
- paths - All system paths
- core - Core settings
- debug - Debug configuration
- output - Output preferences
- providers - Provider settings
- platform - Platform services
- secrets - Secret management
- kms - Key management
- sops - SOPS configuration
- taskservs - Task service paths
- clusters - Cluster paths
- cache - Cache settings
### 2. provider-aws.toml.template
AWS provider configuration template. Generates: `{workspace}/config/providers/aws.toml`
**Variables:**
- `{{workspace.name}}` - Workspace name
- `{{workspace.path}}` - Absolute workspace path
- `{{now.iso}}` - Timestamp
**Sections:**
- provider - Provider metadata
- provider.auth - AWS authentication
- provider.paths - Provider-specific paths
- provider.api - API settings
### 3. provider-local.toml.template
Local provider configuration template. Generates: `{workspace}/config/providers/local.toml`
**Variables:**
- `{{workspace.name}}` - Workspace name
- `{{workspace.path}}` - Absolute workspace path
- `{{now.iso}}` - Timestamp
**Sections:**
- provider - Provider metadata
- provider.auth - Local auth (minimal)
- provider.paths - Provider-specific paths
### 4. provider-upcloud.toml.template
UpCloud provider configuration template. Generates: `{workspace}/config/providers/upcloud.toml`
**Variables:**
- `{{workspace.name}}` - Workspace name
- `{{workspace.path}}` - Absolute workspace path
- `{{now.iso}}` - Timestamp
**Sections:**
- provider - Provider metadata
- provider.auth - UpCloud authentication
- provider.paths - Provider-specific paths
- provider.api - API settings (UpCloud API URL)
### 5. kms.toml.template
Key Management Service configuration template. Generates: `{workspace}/config/kms.toml`
**Variables:**
- `{{workspace.name}}` - Workspace name
- `{{workspace.path}}` - Absolute workspace path
- `{{now.iso}}` - Timestamp
**Sections:**
- kms - KMS mode and settings
- kms.local - Local KMS (Age)
- kms.remote - Remote KMS server
### 6. user-context.yaml.template
User context configuration template. Generates: `~/Library/Application Support/provisioning/ws_{name}.yaml`
**Variables:**
- `{{workspace.name}}` - Workspace name
- `{{workspace.path}}` - Absolute workspace path
- `{{now.iso}}` - Timestamp
**Sections:**
- workspace - Workspace reference
- debug - User debug overrides
- output - User output preferences
- providers - User provider preferences
- paths - User path overrides
## Template Variable Syntax
Templates use `{{variable}}` syntax for interpolation:
```yaml
# Example
workspace:
name: "{{workspace.name}}"
path: "{{workspace.path}}"
created: "{{now.iso}}"
```
## Supported Variables
### Core Variables
- `{{workspace.name}}` - Workspace name (string)
- `{{workspace.path}}` - Absolute workspace path (string)
### Timestamp Variables
- `{{now.iso}}` - ISO 8601 timestamp (YYYY-MM-DDTHH:MM:SSZ)
- `{{now.date}}` - Date only (YYYY-MM-DD)
- `{{now.timestamp}}` - Unix timestamp
### Environment Variables (safe list)
- `{{env.HOME}}` - User home directory
- `{{env.USER}}` - Current user
- `{{env.HOSTNAME}}` - System hostname
## Usage
### Generate Workspace from Template
```nushell
use provisioning/core/nulib/lib_provisioning/workspace/init.nu *
# Initialize workspace with AWS and Local providers
workspace-init "my-workspace" "/path/to/workspace" \
--providers ["aws" "local"] \
--activate
```
### What Happens
1. Templates are read from this directory
2. Variables are interpolated with actual values
3. Generated configs are saved to workspace
4. User context (if --activate) is created
### Generated Structure
```
/path/to/workspace/
├── config/
│ ├── provisioning.yaml # From workspace-provisioning.yaml.template
│ ├── kms.toml # From kms.toml.template
│ └── providers/
│ ├── aws.toml # From provider-aws.toml.template
│ └── local.toml # From provider-local.toml.template
~/Library/Application Support/provisioning/
└── ws_my-workspace.yaml # From user-context.yaml.template
```
## Adding New Templates
### 1. Create Template File
```bash
# Example: New provider template
touch provider-gcp.toml.template
```
### 2. Add Template Content
```toml
# GCP Provider Configuration for Workspace: {{workspace.name}}
# Generated: {{now.iso}}
[provider]
name = "gcp"
enabled = true
workspace = "{{workspace.name}}"
[provider.auth]
project = "default"
region = "us-central1"
[provider.paths]
base = "{{workspace.path}}/.providers/gcp"
cache = "{{workspace.path}}/.providers/gcp/cache"
```
### 3. Update Workspace Init
Add GCP template handling to `workspace/init.nu` :
```nushell
def generate-provider-config [
workspace_path: string
workspace_name: string
provider_name: string
] {
let template_path = $"/path/to/templates/provider-($provider_name).toml.template"
if not ($template_path | path exists) {
print $"⚠️ No template for provider '($provider_name)'"
return
}
# Generate config...
}
```
## Template Best Practices
### 1. Always Include Metadata
```yaml
# Configuration for Workspace: {{workspace.name}}
# Generated: {{now.iso}}
# DO NOT EDIT - Regenerate from template
```
### 2. Use Absolute Paths
```yaml
paths:
base: "{{workspace.path}}" # ✅ Absolute
cache: "{{workspace.path}}/.cache" # ✅ Absolute
# NOT relative:
# cache: ".cache" # ❌ Relative
```
### 3. Provide Sensible Defaults
```yaml
debug:
enabled: false # Safe default
log_level: "info" # Reasonable default
providers:
default: "local" # Safe default
```
### 4. Document Sections
```yaml
# Debug settings (can be overridden by user context)
debug:
enabled: false
log_level: "info"
```
### 5. Group Related Settings
```toml
[kms]
mode = "local"
[kms.local]
provider = "age"
key_path = "{{workspace.path}}/.kms/keys/age.txt"
[kms.remote]
server = ""
```
## Validation
Templates should be validated before use:
1. **Syntax Valid** : YAML/TOML parseable
2. **Variables Complete** : All `{{variables}}` have values
3. **Paths Absolute** : All paths use `{{workspace.path}}`
4. **Sensible Defaults** : Safe, secure defaults
## Troubleshooting
### Template Not Found
```
⚠️ Warning: No template found for provider 'xyz'
```
**Solution**: Create template or check provider name spelling.
### Variable Not Interpolated
Config shows `{{workspace.name}}` instead of actual name.
**Solution**: Check variable exists in interpolation list, update workspace/init.nu.
### Invalid YAML/TOML
Generated config fails to parse.
**Solution**: Validate template syntax, ensure proper escaping.
## Related Files
- **Workspace Init**: `provisioning/core/nulib/lib_provisioning/workspace/init.nu`
- **Config Loader**: `provisioning/core/nulib/lib_provisioning/config/loader.nu`
- **Documentation**: `docs/configuration/workspace-config-architecture.md`
## Summary
- Templates are **source files only** , never loaded at runtime
- Used to **generate workspace configs** during initialization
- Support **variable interpolation** with `{{variable}}` syntax
- Each template creates specific config file in workspace
- **Modify templates** to change default workspace structure