|
| 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