# Configuration Templates **Purpose**: Template files for generating workspace configurations ## 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. ## 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