|  | 
|  | 1 | +# Environment vs Provider Configuration Analysis | 
|  | 2 | + | 
|  | 3 | +## Overview | 
|  | 4 | + | 
|  | 5 | +This document analyzes how the Torrust Tracker Demo infrastructure handles multiple | 
|  | 6 | +environments (staging, production) using the same cloud provider (Hetzner) and explains | 
|  | 7 | +why there are no file conflicts in the OpenTofu/Terraform configuration. | 
|  | 8 | + | 
|  | 9 | +## Problem Statement | 
|  | 10 | + | 
|  | 11 | +The user was concerned about potential configuration conflicts when multiple environments | 
|  | 12 | +use the same provider, specifically: | 
|  | 13 | + | 
|  | 14 | +- Why both `staging-hetzner.env` and `production-hetzner.env` can coexist | 
|  | 15 | +- How the system handles generating provider-specific Terraform variables | 
|  | 16 | +- Whether files get overwritten causing conflicts | 
|  | 17 | + | 
|  | 18 | +## Key Findings | 
|  | 19 | + | 
|  | 20 | +### 1. Dynamic Variable Generation (Not Static Files) | 
|  | 21 | + | 
|  | 22 | +**The system does NOT create separate static `.auto.tfvars` files for each environment.** | 
|  | 23 | +Instead, it uses a **dynamic generation approach** where: | 
|  | 24 | + | 
|  | 25 | +- Each deployment generates the `.auto.tfvars` file on-demand | 
|  | 26 | +- The same file (`hetzner.auto.tfvars`) is overwritten with environment-specific values | 
|  | 27 | +- Variables are sourced from the loaded environment configuration | 
|  | 28 | + | 
|  | 29 | +### 2. File Structure Analysis | 
|  | 30 | + | 
|  | 31 | +```text | 
|  | 32 | +infrastructure/ | 
|  | 33 | +├── config/ | 
|  | 34 | +│   └── environments/ | 
|  | 35 | +│       ├── staging-hetzner.env      # Environment-specific config | 
|  | 36 | +│       └── production-hetzner.env   # Environment-specific config | 
|  | 37 | +└── terraform/ | 
|  | 38 | +    └── hetzner.auto.tfvars          # Single provider file (overwritten) | 
|  | 39 | +``` | 
|  | 40 | + | 
|  | 41 | +### 3. How It Works | 
|  | 42 | + | 
|  | 43 | +1. **Environment Loading**: The system loads one environment at a time: | 
|  | 44 | + | 
|  | 45 | +   ```bash | 
|  | 46 | +   source infrastructure/config/environments/staging-hetzner.env | 
|  | 47 | +   ``` | 
|  | 48 | + | 
|  | 49 | +2. **Provider Loading**: The provider interface validates and loads the provider: | 
|  | 50 | + | 
|  | 51 | +   ```bash | 
|  | 52 | +   load_provider hetzner | 
|  | 53 | +   ``` | 
|  | 54 | + | 
|  | 55 | +3. **Dynamic Generation**: The provider generates variables into a single file: | 
|  | 56 | + | 
|  | 57 | +   ```bash | 
|  | 58 | +   provider_generate_terraform_vars "${TERRAFORM_DIR}/hetzner.auto.tfvars" | 
|  | 59 | +   ``` | 
|  | 60 | + | 
|  | 61 | +4. **Terraform Execution**: OpenTofu/Terraform uses the generated variables for that | 
|  | 62 | +   specific deployment | 
|  | 63 | + | 
|  | 64 | +## Test Results | 
|  | 65 | + | 
|  | 66 | +### Staging Environment Variables | 
|  | 67 | + | 
|  | 68 | +```hcl | 
|  | 69 | +# Generated Hetzner Cloud provider variables | 
|  | 70 | +infrastructure_provider = "hetzner" | 
|  | 71 | +
 | 
|  | 72 | +# Standard VM configuration | 
|  | 73 | +environment        = "development" | 
|  | 74 | +vm_name           = "torrust-tracker-staging" | 
|  | 75 | +vm_memory         = 4096 | 
|  | 76 | +vm_vcpus          = 4 | 
|  | 77 | +vm_disk_size      = 30 | 
|  | 78 | +ssh_public_key    = "ssh-rsa AAAAB3..." | 
|  | 79 | +use_minimal_config = false | 
|  | 80 | +
 | 
|  | 81 | +# Hetzner-specific settings | 
|  | 82 | +hetzner_token       = "staging_token_123" | 
|  | 83 | +hetzner_server_type = "cx31" | 
|  | 84 | +hetzner_location    = "nbg1" | 
|  | 85 | +hetzner_image       = "ubuntu-24.04" | 
|  | 86 | +``` | 
|  | 87 | + | 
|  | 88 | +### Production Environment Variables | 
|  | 89 | + | 
|  | 90 | +```hcl | 
|  | 91 | +# Generated Hetzner Cloud provider variables | 
|  | 92 | +infrastructure_provider = "hetzner" | 
|  | 93 | +
 | 
|  | 94 | +# Standard VM configuration | 
|  | 95 | +environment        = "development" | 
|  | 96 | +vm_name           = "torrust-tracker-production" | 
|  | 97 | +vm_memory         = 8192 | 
|  | 98 | +vm_vcpus          = 4 | 
|  | 99 | +vm_disk_size      = 40 | 
|  | 100 | +ssh_public_key    = "ssh-rsa AAAAB3..." | 
|  | 101 | +use_minimal_config = false | 
|  | 102 | +
 | 
|  | 103 | +# Hetzner-specific settings | 
|  | 104 | +hetzner_token       = "production_token_456" | 
|  | 105 | +hetzner_server_type = "cx41" | 
|  | 106 | +hetzner_location    = "nbg1" | 
|  | 107 | +hetzner_image       = "ubuntu-24.04" | 
|  | 108 | +``` | 
|  | 109 | + | 
|  | 110 | +### Key Differences | 
|  | 111 | + | 
|  | 112 | +- **VM Name**: `torrust-tracker-staging` vs `torrust-tracker-production` | 
|  | 113 | +- **Memory**: 4096MB vs 8192MB | 
|  | 114 | +- **Disk Size**: 30GB vs 40GB | 
|  | 115 | +- **Server Type**: cx31 vs cx41 (auto-selected based on memory) | 
|  | 116 | +- **Token**: Different API tokens for each environment | 
|  | 117 | + | 
|  | 118 | +## Overwrite Behavior Demonstration | 
|  | 119 | + | 
|  | 120 | +When switching environments, the system overwrites the same file with new values: | 
|  | 121 | + | 
|  | 122 | +1. **Deploy Staging**: Generates `hetzner.auto.tfvars` with staging values | 
|  | 123 | +2. **Deploy Production**: Overwrites `hetzner.auto.tfvars` with production values | 
|  | 124 | + | 
|  | 125 | +This approach ensures: | 
|  | 126 | + | 
|  | 127 | +- ✅ No file conflicts between environments | 
|  | 128 | +- ✅ Only one set of variables active at a time | 
|  | 129 | +- ✅ Environment-specific configurations are properly isolated | 
|  | 130 | +- ✅ No risk of mixed configurations | 
|  | 131 | + | 
|  | 132 | +## Code Flow | 
|  | 133 | + | 
|  | 134 | +```mermaid | 
|  | 135 | +graph TD | 
|  | 136 | +    A[make infra-apply ENVIRONMENT=staging PROVIDER=hetzner] --> B[Load staging-hetzner.env] | 
|  | 137 | +    B --> C[Load hetzner provider] | 
|  | 138 | +    C --> D[Generate hetzner.auto.tfvars with staging values] | 
|  | 139 | +    D --> E[Run OpenTofu apply with staging configuration] | 
|  | 140 | +
 | 
|  | 141 | +    F[make infra-apply ENVIRONMENT=production PROVIDER=hetzner] --> G[Load production-hetzner.env] | 
|  | 142 | +    G --> H[Load hetzner provider] | 
|  | 143 | +    H --> I[Overwrite hetzner.auto.tfvars with production values] | 
|  | 144 | +    I --> J[Run OpenTofu apply with production configuration] | 
|  | 145 | +``` | 
|  | 146 | + | 
|  | 147 | +## Provider Interface Implementation | 
|  | 148 | + | 
|  | 149 | +### File: `infrastructure/scripts/providers/provider-interface.sh` | 
|  | 150 | + | 
|  | 151 | +- Defines standard interface all providers must implement | 
|  | 152 | +- Validates provider implementations | 
|  | 153 | +- Manages provider loading and variable generation | 
|  | 154 | + | 
|  | 155 | +### File: `infrastructure/scripts/providers/hetzner/provider.sh` | 
|  | 156 | + | 
|  | 157 | +- Implements `provider_generate_terraform_vars()` function | 
|  | 158 | +- Reads environment variables and generates Terraform variables | 
|  | 159 | +- Auto-selects server types based on memory requirements | 
|  | 160 | +- Handles SSH key configuration | 
|  | 161 | + | 
|  | 162 | +### File: `infrastructure/scripts/provision-infrastructure.sh` | 
|  | 163 | + | 
|  | 164 | +- Orchestrates the entire deployment process | 
|  | 165 | +- Loads environment configurations | 
|  | 166 | +- Calls provider variable generation | 
|  | 167 | +- Executes OpenTofu/Terraform operations | 
|  | 168 | + | 
|  | 169 | +## Conclusion | 
|  | 170 | + | 
|  | 171 | +The system design is **safe and conflict-free** because: | 
|  | 172 | + | 
|  | 173 | +1. **Sequential Execution**: Only one environment is deployed at a time | 
|  | 174 | +2. **Dynamic Generation**: Variables are generated fresh for each deployment | 
|  | 175 | +3. **Single File Overwrite**: The same provider file is reused and overwritten | 
|  | 176 | +4. **Environment Isolation**: Each environment has its own configuration file | 
|  | 177 | + | 
|  | 178 | +There is **no risk of file conflicts** because the system intentionally overwrites the | 
|  | 179 | +same file with environment-specific values, ensuring that only the current deployment's | 
|  | 180 | +configuration is active. | 
|  | 181 | + | 
|  | 182 | +This approach follows infrastructure best practices by: | 
|  | 183 | + | 
|  | 184 | +- Maintaining clear separation between environments | 
|  | 185 | +- Preventing configuration drift | 
|  | 186 | +- Ensuring reproducible deployments | 
|  | 187 | +- Following the principle of least surprise | 
|  | 188 | + | 
|  | 189 | +## Testing Commands Used | 
|  | 190 | + | 
|  | 191 | +```bash | 
|  | 192 | +# Load staging environment and generate variables | 
|  | 193 | +source infrastructure/config/environments/staging-hetzner.env | 
|  | 194 | +load_provider hetzner | 
|  | 195 | +export HETZNER_API_TOKEN="staging_token_123" | 
|  | 196 | +provider_generate_terraform_vars "/tmp/staging-test.auto.tfvars" | 
|  | 197 | + | 
|  | 198 | +# Load production environment and generate variables | 
|  | 199 | +source infrastructure/config/environments/production-hetzner.env | 
|  | 200 | +load_provider hetzner | 
|  | 201 | +export HETZNER_API_TOKEN="production_token_456" | 
|  | 202 | +provider_generate_terraform_vars "/tmp/production-test.auto.tfvars" | 
|  | 203 | + | 
|  | 204 | +# Compare the differences | 
|  | 205 | +diff -u /tmp/staging-test.auto.tfvars /tmp/production-test.auto.tfvars | 
|  | 206 | + | 
|  | 207 | +# Test overwrite behavior | 
|  | 208 | +provider_generate_terraform_vars "/tmp/same-file.auto.tfvars" | 
|  | 209 | +# Switch environment and overwrite | 
|  | 210 | +provider_generate_terraform_vars "/tmp/same-file.auto.tfvars" | 
|  | 211 | +``` | 
0 commit comments