471 lines
10 KiB
Markdown
471 lines
10 KiB
Markdown
|
|
# Migration Guide: Environment Variables to TOML Configuration
|
||
|
|
|
||
|
|
This guide helps you migrate from the old environment variable-only configuration system to the new TOML-based configuration system with environment variable overrides.
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
The new configuration system provides:
|
||
|
|
- **TOML files** for structured configuration
|
||
|
|
- **Environment variable overrides** for sensitive data
|
||
|
|
- **Environment-specific configs** (dev, prod, etc.)
|
||
|
|
- **Validation and error handling**
|
||
|
|
- **Better organization** of settings
|
||
|
|
|
||
|
|
## Migration Steps
|
||
|
|
|
||
|
|
### Step 1: Identify Current Configuration
|
||
|
|
|
||
|
|
First, identify all environment variables currently used in your application:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# List all environment variables starting with common prefixes
|
||
|
|
env | grep -E "^(SERVER_|DATABASE_|SESSION_|CORS_|TLS_|OAUTH_|SMTP_|REDIS_|LOG_)" | sort
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 2: Create Base Configuration File
|
||
|
|
|
||
|
|
Create a `config.toml` file with your current settings:
|
||
|
|
|
||
|
|
```toml
|
||
|
|
# config.toml
|
||
|
|
[server]
|
||
|
|
protocol = "http"
|
||
|
|
host = "127.0.0.1"
|
||
|
|
port = 3030
|
||
|
|
environment = "development"
|
||
|
|
log_level = "info"
|
||
|
|
|
||
|
|
[database]
|
||
|
|
url = "postgresql://localhost:5432/myapp"
|
||
|
|
max_connections = 10
|
||
|
|
min_connections = 1
|
||
|
|
connect_timeout = 30
|
||
|
|
idle_timeout = 600
|
||
|
|
max_lifetime = 1800
|
||
|
|
|
||
|
|
[session]
|
||
|
|
secret = "change-this-in-production"
|
||
|
|
cookie_name = "session_id"
|
||
|
|
cookie_secure = false
|
||
|
|
cookie_http_only = true
|
||
|
|
cookie_same_site = "lax"
|
||
|
|
max_age = 3600
|
||
|
|
|
||
|
|
[cors]
|
||
|
|
allowed_origins = ["http://localhost:3030"]
|
||
|
|
allowed_methods = ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
|
||
|
|
allowed_headers = ["Content-Type", "Authorization"]
|
||
|
|
allow_credentials = true
|
||
|
|
max_age = 3600
|
||
|
|
|
||
|
|
[security]
|
||
|
|
enable_csrf = true
|
||
|
|
csrf_token_name = "csrf_token"
|
||
|
|
rate_limit_requests = 100
|
||
|
|
rate_limit_window = 60
|
||
|
|
bcrypt_cost = 12
|
||
|
|
|
||
|
|
[static]
|
||
|
|
assets_dir = "public"
|
||
|
|
site_root = "target/site"
|
||
|
|
site_pkg_dir = "pkg"
|
||
|
|
|
||
|
|
[oauth]
|
||
|
|
enabled = false
|
||
|
|
|
||
|
|
[email]
|
||
|
|
enabled = false
|
||
|
|
smtp_host = "localhost"
|
||
|
|
smtp_port = 587
|
||
|
|
smtp_username = ""
|
||
|
|
smtp_password = ""
|
||
|
|
from_email = "noreply@example.com"
|
||
|
|
from_name = "My App"
|
||
|
|
|
||
|
|
[redis]
|
||
|
|
enabled = false
|
||
|
|
url = "redis://localhost:6379"
|
||
|
|
pool_size = 10
|
||
|
|
connection_timeout = 5
|
||
|
|
command_timeout = 5
|
||
|
|
|
||
|
|
[app]
|
||
|
|
name = "My Rust App"
|
||
|
|
version = "0.1.0"
|
||
|
|
debug = true
|
||
|
|
enable_metrics = false
|
||
|
|
enable_health_check = true
|
||
|
|
enable_compression = true
|
||
|
|
max_request_size = 10485760
|
||
|
|
|
||
|
|
[logging]
|
||
|
|
format = "text"
|
||
|
|
level = "info"
|
||
|
|
file_path = "logs/app.log"
|
||
|
|
max_file_size = 10485760
|
||
|
|
max_files = 5
|
||
|
|
enable_console = true
|
||
|
|
enable_file = false
|
||
|
|
|
||
|
|
[content]
|
||
|
|
enabled = false
|
||
|
|
content_dir = "content"
|
||
|
|
cache_enabled = true
|
||
|
|
cache_ttl = 3600
|
||
|
|
max_file_size = 5242880
|
||
|
|
|
||
|
|
[features]
|
||
|
|
auth = true
|
||
|
|
tls = false
|
||
|
|
content_db = true
|
||
|
|
two_factor_auth = false
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 3: Update Code to Use New Configuration
|
||
|
|
|
||
|
|
Replace the old configuration loading:
|
||
|
|
|
||
|
|
```rust
|
||
|
|
// OLD: Environment-only configuration
|
||
|
|
use config::ServerConfig;
|
||
|
|
|
||
|
|
let server_config = ServerConfig::from_env()?;
|
||
|
|
let addr = server_config.server_address();
|
||
|
|
let log_level = server_config.log_level;
|
||
|
|
```
|
||
|
|
|
||
|
|
With the new configuration system:
|
||
|
|
|
||
|
|
```rust
|
||
|
|
// NEW: TOML + Environment configuration
|
||
|
|
use config::Config;
|
||
|
|
|
||
|
|
let config = Config::load()?;
|
||
|
|
let addr = config.server_address();
|
||
|
|
let log_level = config.server.log_level;
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 4: Environment Variable Mapping
|
||
|
|
|
||
|
|
Map your existing environment variables to the new system:
|
||
|
|
|
||
|
|
| Old Environment Variable | New TOML Location | Environment Override |
|
||
|
|
|-------------------------|-------------------|---------------------|
|
||
|
|
| `SERVER_HOST` | `server.host` | `SERVER_HOST` |
|
||
|
|
| `SERVER_PORT` | `server.port` | `SERVER_PORT` |
|
||
|
|
| `SERVER_PROTOCOL` | `server.protocol` | `SERVER_PROTOCOL` |
|
||
|
|
| `DATABASE_URL` | `database.url` | `DATABASE_URL` |
|
||
|
|
| `SESSION_SECRET` | `session.secret` | `SESSION_SECRET` |
|
||
|
|
| `LOG_LEVEL` | `server.log_level` | `LOG_LEVEL` |
|
||
|
|
| `ENVIRONMENT` | `server.environment` | `ENVIRONMENT` |
|
||
|
|
| `TLS_CERT_PATH` | `server.tls.cert_path` | `TLS_CERT_PATH` |
|
||
|
|
| `TLS_KEY_PATH` | `server.tls.key_path` | `TLS_KEY_PATH` |
|
||
|
|
|
||
|
|
### Step 5: Handle Sensitive Data
|
||
|
|
|
||
|
|
Move sensitive data to environment variables and use substitution:
|
||
|
|
|
||
|
|
```toml
|
||
|
|
# config.toml - Use environment variable substitution
|
||
|
|
[database]
|
||
|
|
url = "postgresql://user:${DATABASE_PASSWORD}@localhost:5432/myapp"
|
||
|
|
|
||
|
|
[session]
|
||
|
|
secret = "${SESSION_SECRET}"
|
||
|
|
|
||
|
|
[oauth.google]
|
||
|
|
client_id = "${GOOGLE_CLIENT_ID}"
|
||
|
|
client_secret = "${GOOGLE_CLIENT_SECRET}"
|
||
|
|
|
||
|
|
[email]
|
||
|
|
smtp_username = "${SMTP_USERNAME}"
|
||
|
|
smtp_password = "${SMTP_PASSWORD}"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 6: Create Environment-Specific Configurations
|
||
|
|
|
||
|
|
Create separate configuration files for different environments:
|
||
|
|
|
||
|
|
**config.dev.toml:**
|
||
|
|
```toml
|
||
|
|
[server]
|
||
|
|
protocol = "http"
|
||
|
|
host = "127.0.0.1"
|
||
|
|
port = 3030
|
||
|
|
environment = "development"
|
||
|
|
log_level = "debug"
|
||
|
|
|
||
|
|
[database]
|
||
|
|
url = "postgresql://dev:dev@localhost:5432/myapp_dev"
|
||
|
|
max_connections = 5
|
||
|
|
|
||
|
|
[security]
|
||
|
|
enable_csrf = false
|
||
|
|
rate_limit_requests = 1000
|
||
|
|
bcrypt_cost = 4
|
||
|
|
|
||
|
|
[session]
|
||
|
|
cookie_secure = false
|
||
|
|
max_age = 7200
|
||
|
|
```
|
||
|
|
|
||
|
|
**config.prod.toml:**
|
||
|
|
```toml
|
||
|
|
[server]
|
||
|
|
protocol = "https"
|
||
|
|
host = "0.0.0.0"
|
||
|
|
port = 443
|
||
|
|
environment = "production"
|
||
|
|
log_level = "info"
|
||
|
|
|
||
|
|
[server.tls]
|
||
|
|
cert_path = "/etc/ssl/certs/app.crt"
|
||
|
|
key_path = "/etc/ssl/private/app.key"
|
||
|
|
|
||
|
|
[database]
|
||
|
|
url = "postgresql://prod:${DATABASE_PASSWORD}@db.example.com:5432/myapp_prod"
|
||
|
|
max_connections = 20
|
||
|
|
|
||
|
|
[security]
|
||
|
|
enable_csrf = true
|
||
|
|
rate_limit_requests = 50
|
||
|
|
bcrypt_cost = 12
|
||
|
|
|
||
|
|
[session]
|
||
|
|
secret = "${SESSION_SECRET}"
|
||
|
|
cookie_secure = true
|
||
|
|
cookie_same_site = "strict"
|
||
|
|
max_age = 3600
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 7: Update Deployment Scripts
|
||
|
|
|
||
|
|
Update your deployment scripts to use the new configuration system:
|
||
|
|
|
||
|
|
**Docker:**
|
||
|
|
```dockerfile
|
||
|
|
# OLD
|
||
|
|
ENV SERVER_HOST=0.0.0.0
|
||
|
|
ENV SERVER_PORT=8080
|
||
|
|
ENV DATABASE_URL=postgresql://...
|
||
|
|
ENV SESSION_SECRET=...
|
||
|
|
|
||
|
|
# NEW
|
||
|
|
COPY config.prod.toml /app/config.toml
|
||
|
|
ENV ENVIRONMENT=production
|
||
|
|
ENV DATABASE_PASSWORD=...
|
||
|
|
ENV SESSION_SECRET=...
|
||
|
|
```
|
||
|
|
|
||
|
|
**Kubernetes:**
|
||
|
|
```yaml
|
||
|
|
# OLD
|
||
|
|
env:
|
||
|
|
- name: SERVER_HOST
|
||
|
|
value: "0.0.0.0"
|
||
|
|
- name: SERVER_PORT
|
||
|
|
value: "8080"
|
||
|
|
- name: DATABASE_URL
|
||
|
|
valueFrom:
|
||
|
|
secretKeyRef:
|
||
|
|
name: app-secrets
|
||
|
|
key: database-url
|
||
|
|
|
||
|
|
# NEW
|
||
|
|
env:
|
||
|
|
- name: ENVIRONMENT
|
||
|
|
value: "production"
|
||
|
|
- name: DATABASE_PASSWORD
|
||
|
|
valueFrom:
|
||
|
|
secretKeyRef:
|
||
|
|
name: app-secrets
|
||
|
|
key: database-password
|
||
|
|
- name: SESSION_SECRET
|
||
|
|
valueFrom:
|
||
|
|
secretKeyRef:
|
||
|
|
name: app-secrets
|
||
|
|
key: session-secret
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 8: Update Environment Files
|
||
|
|
|
||
|
|
Update your `.env` files to work with the new system:
|
||
|
|
|
||
|
|
**.env.development:**
|
||
|
|
```bash
|
||
|
|
# Environment
|
||
|
|
ENVIRONMENT=development
|
||
|
|
|
||
|
|
# Database
|
||
|
|
DATABASE_URL=postgresql://dev:dev@localhost:5432/myapp_dev
|
||
|
|
|
||
|
|
# Session
|
||
|
|
SESSION_SECRET=dev-secret-not-for-production
|
||
|
|
|
||
|
|
# OAuth (optional)
|
||
|
|
GOOGLE_CLIENT_ID=your-dev-google-client-id
|
||
|
|
GOOGLE_CLIENT_SECRET=your-dev-google-client-secret
|
||
|
|
```
|
||
|
|
|
||
|
|
**.env.production:**
|
||
|
|
```bash
|
||
|
|
# Environment
|
||
|
|
ENVIRONMENT=production
|
||
|
|
|
||
|
|
# Database
|
||
|
|
DATABASE_PASSWORD=your-production-database-password
|
||
|
|
|
||
|
|
# Session
|
||
|
|
SESSION_SECRET=your-super-secret-production-key
|
||
|
|
|
||
|
|
# OAuth
|
||
|
|
GOOGLE_CLIENT_ID=your-production-google-client-id
|
||
|
|
GOOGLE_CLIENT_SECRET=your-production-google-client-secret
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 9: Test the Migration
|
||
|
|
|
||
|
|
1. **Validate configuration:**
|
||
|
|
```bash
|
||
|
|
cargo run --bin config_tool -- validate
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Show current configuration:**
|
||
|
|
```bash
|
||
|
|
cargo run --bin config_tool -- show
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Check environment variables:**
|
||
|
|
```bash
|
||
|
|
cargo run --bin config_tool -- check-env
|
||
|
|
```
|
||
|
|
|
||
|
|
4. **Run your application:**
|
||
|
|
```bash
|
||
|
|
cargo run
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 10: Update Documentation
|
||
|
|
|
||
|
|
Update your project documentation to reflect the new configuration system:
|
||
|
|
|
||
|
|
1. Update README.md with configuration instructions
|
||
|
|
2. Document required environment variables
|
||
|
|
3. Provide example configuration files
|
||
|
|
4. Update deployment guides
|
||
|
|
|
||
|
|
## Common Migration Issues
|
||
|
|
|
||
|
|
### Issue 1: Configuration Not Found
|
||
|
|
|
||
|
|
**Error:**
|
||
|
|
```
|
||
|
|
Configuration file not found: config.toml
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution:**
|
||
|
|
Create a configuration file or set the `CONFIG_FILE` environment variable:
|
||
|
|
```bash
|
||
|
|
cp config.dev.toml config.toml
|
||
|
|
# or
|
||
|
|
export CONFIG_FILE=/path/to/your/config.toml
|
||
|
|
```
|
||
|
|
|
||
|
|
### Issue 2: Environment Variable Substitution
|
||
|
|
|
||
|
|
**Error:**
|
||
|
|
```
|
||
|
|
Environment variable 'DATABASE_PASSWORD' not found
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution:**
|
||
|
|
Set the required environment variable:
|
||
|
|
```bash
|
||
|
|
export DATABASE_PASSWORD=your-password
|
||
|
|
```
|
||
|
|
|
||
|
|
### Issue 3: TLS Configuration
|
||
|
|
|
||
|
|
**Error:**
|
||
|
|
```
|
||
|
|
TLS certificate path is required when using HTTPS
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution:**
|
||
|
|
Either disable HTTPS or provide certificate paths:
|
||
|
|
```toml
|
||
|
|
[server]
|
||
|
|
protocol = "http" # Disable HTTPS
|
||
|
|
# or
|
||
|
|
protocol = "https"
|
||
|
|
[server.tls]
|
||
|
|
cert_path = "/path/to/cert.crt"
|
||
|
|
key_path = "/path/to/key.key"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Issue 4: Database Connection
|
||
|
|
|
||
|
|
**Error:**
|
||
|
|
```
|
||
|
|
Failed to connect to database
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solution:**
|
||
|
|
Check your database URL format and ensure the database is running:
|
||
|
|
```toml
|
||
|
|
[database]
|
||
|
|
url = "postgresql://username:password@host:port/database"
|
||
|
|
```
|
||
|
|
|
||
|
|
## Migration Checklist
|
||
|
|
|
||
|
|
- [ ] Identify all current environment variables
|
||
|
|
- [ ] Create base `config.toml` file
|
||
|
|
- [ ] Update code to use `Config::load()`
|
||
|
|
- [ ] Create environment-specific config files
|
||
|
|
- [ ] Move sensitive data to environment variables
|
||
|
|
- [ ] Update deployment scripts
|
||
|
|
- [ ] Update `.env` files
|
||
|
|
- [ ] Test configuration loading
|
||
|
|
- [ ] Validate configuration
|
||
|
|
- [ ] Update documentation
|
||
|
|
- [ ] Update CI/CD pipelines
|
||
|
|
- [ ] Train team on new configuration system
|
||
|
|
|
||
|
|
## Rollback Plan
|
||
|
|
|
||
|
|
If you need to rollback to the old system:
|
||
|
|
|
||
|
|
1. Keep the old configuration loading code in a separate branch
|
||
|
|
2. Maintain both systems during transition period
|
||
|
|
3. Use feature flags to switch between systems
|
||
|
|
4. Document the rollback process
|
||
|
|
|
||
|
|
```rust
|
||
|
|
// Rollback configuration loading
|
||
|
|
#[cfg(feature = "legacy-config")]
|
||
|
|
let config = ServerConfig::from_env()?;
|
||
|
|
|
||
|
|
#[cfg(not(feature = "legacy-config"))]
|
||
|
|
let config = Config::load()?;
|
||
|
|
```
|
||
|
|
|
||
|
|
## Best Practices After Migration
|
||
|
|
|
||
|
|
1. **Version control:** Keep configuration files in version control (except sensitive production configs)
|
||
|
|
2. **Environment parity:** Ensure dev/staging/prod configurations are consistent
|
||
|
|
3. **Documentation:** Keep configuration documentation up to date
|
||
|
|
4. **Validation:** Regularly validate configuration files
|
||
|
|
5. **Secrets management:** Use proper secrets management for production
|
||
|
|
6. **Monitoring:** Monitor configuration changes in production
|
||
|
|
7. **Testing:** Test configuration loading in CI/CD
|
||
|
|
8. **Backup:** Backup configuration files regularly
|
||
|
|
|
||
|
|
## Getting Help
|
||
|
|
|
||
|
|
If you encounter issues during migration:
|
||
|
|
|
||
|
|
1. Run the configuration tool: `cargo run --bin config_tool -- help`
|
||
|
|
2. Check the configuration examples in the repository
|
||
|
|
3. Review the CONFIG_README.md for detailed documentation
|
||
|
|
4. Open an issue on the project repository
|