Improve basic example to showcase all resolver types
- Replace simple ENV-only example with diverse resolver showcase - Add GCP Secret Manager, AWS Secrets Manager, Vault examples in main config - Add JSON resolver example for loading service configs and feature flags - Add Consul, K8S, and FILE resolvers for different config aspects - Include example JSON files to show what JSON resolver references - Update Go code to demonstrate accessing the various config types - Move away from env-centric example to show full power upfront
This commit is contained in:
parent
81eddf2b38
commit
e6db26d2c4
135
README.md
135
README.md
@ -40,33 +40,49 @@ go get git.eeqj.de/sneak/smartconfig
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# config.yaml
|
# config.yaml
|
||||||
# Example with top-level values for easy access
|
# Showcase the power of multiple resolver types
|
||||||
app_name: ${ENV:APP_NAME}
|
app_name: ${ENV:APP_NAME}
|
||||||
port: ${ENV:PORT} # Becomes number 8080 if PORT="8080"
|
version: ${FILE:/app/VERSION} # Read from file
|
||||||
debug_mode: ${ENV:DEBUG_MODE} # Becomes boolean true if DEBUG_MODE="true"
|
port: ${ENV:PORT} # Number if PORT="8080"
|
||||||
cache_size_bytes: ${ENV:CACHE_SIZE} # Human-readable: "100MB", "1.5GB"
|
debug_mode: ${ENV:DEBUG_MODE} # Boolean if "true"/"false"
|
||||||
|
|
||||||
# Nested configuration for organization
|
# External service configuration from JSON config file
|
||||||
server:
|
services: ${JSON:/etc/config/services.json:production} # Load entire object
|
||||||
host: localhost
|
|
||||||
port: ${ENV:SERVER_PORT}
|
|
||||||
name: "server-${ENV:INSTANCE_ID}" # Always a string due to mixed content
|
|
||||||
|
|
||||||
|
# API credentials from cloud secret managers
|
||||||
|
api_keys:
|
||||||
|
stripe: ${GCPSM:projects/myproject/secrets/stripe-api-key}
|
||||||
|
sendgrid: ${AWSSM:prod/sendgrid-api-key}
|
||||||
|
twilio: ${VAULT:secret/data/api:twilio_key}
|
||||||
|
datadog: ${ENV:DD_API_KEY} # Fallback to env for local dev
|
||||||
|
|
||||||
|
# Database configuration with mixed sources
|
||||||
database:
|
database:
|
||||||
host: ${ENV:DB_HOST}
|
host: ${CONSUL:service/postgres/address}
|
||||||
port: ${ENV:DB_PORT}
|
port: ${CONSUL:service/postgres/port}
|
||||||
|
name: ${ENV:DB_NAME}
|
||||||
username: ${ENV:DB_USER}
|
username: ${ENV:DB_USER}
|
||||||
password: ${VAULT:secret/data/myapp:db_password}
|
password: ${GCPSM:projects/myproject/secrets/db-password}
|
||||||
|
|
||||||
|
# SSL configuration from multiple sources
|
||||||
|
ssl:
|
||||||
|
enabled: ${ENV:DB_SSL_ENABLED}
|
||||||
|
ca_cert: ${FILE:/etc/ssl/db-ca.crt}
|
||||||
|
client_cert: ${K8SS:default/db-certs:client.crt}
|
||||||
|
|
||||||
features:
|
# Feature flags from JSON file
|
||||||
cache_enabled: ${ENV:CACHE_ENABLED} # Boolean if "true"/"false"
|
features: ${JSON:/etc/config/features.json:${ENV:ENVIRONMENT}}
|
||||||
max_connections: ${ENV:MAX_CONN} # Number if numeric string
|
|
||||||
debug: ${ENV:FEATURES_DEBUG}
|
# Server configuration
|
||||||
|
server:
|
||||||
|
listen: "0.0.0.0:${ENV:PORT}"
|
||||||
|
workers: ${EXEC:nproc} # Dynamic based on CPU cores
|
||||||
|
hostname: ${EXEC:hostname -f}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# These will be exported as environment variables
|
# Export these as environment variables for child processes
|
||||||
API_TOKEN: ${AWSSM:api-token}
|
DATABASE_URL: "postgres://${ENV:DB_USER}:${GCPSM:projects/myproject/secrets/db-password}@${CONSUL:service/postgres/address}:5432/${ENV:DB_NAME}"
|
||||||
SLACK_WEBHOOK: ${FILE:/etc/secrets/slack_webhook.txt}
|
NEW_RELIC_LICENSE: ${AWSSM:monitoring/newrelic-license}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Go Code
|
### Go Code
|
||||||
@ -77,6 +93,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
"git.eeqj.de/sneak/smartconfig"
|
"git.eeqj.de/sneak/smartconfig"
|
||||||
)
|
)
|
||||||
@ -88,29 +105,77 @@ func main() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Access typed values (top-level keys only)
|
// Access typed values
|
||||||
port, err := config.GetInt("port")
|
appName, _ := config.GetString("app_name")
|
||||||
if err != nil {
|
port, _ := config.GetInt("port")
|
||||||
log.Fatal(err)
|
debugMode, _ := config.GetBool("debug_mode")
|
||||||
}
|
|
||||||
|
|
||||||
// For nested values, navigate manually
|
fmt.Printf("Starting %s on port %d (debug: %v)\n", appName, port, debugMode)
|
||||||
|
|
||||||
|
// Access nested values
|
||||||
data := config.Data()
|
data := config.Data()
|
||||||
var debugMode bool
|
|
||||||
if features, ok := data["features"].(map[string]interface{}); ok {
|
// API keys configuration
|
||||||
if debug, ok := features["debug"].(bool); ok {
|
if apiKeys, ok := data["api_keys"].(map[string]interface{}); ok {
|
||||||
debugMode = debug
|
if stripe, ok := apiKeys["stripe"].(string); ok {
|
||||||
|
fmt.Printf("Stripe API key loaded: %s...\n", stripe[:8])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Human-readable byte sizes (top-level)
|
// Database configuration
|
||||||
cacheSize, err := config.GetBytes("cache_size_bytes")
|
if db, ok := data["database"].(map[string]interface{}); ok {
|
||||||
if err != nil {
|
host, _ := db["host"].(string)
|
||||||
log.Fatal(err)
|
port, _ := db["port"].(int)
|
||||||
|
fmt.Printf("Database: %s:%d\n", host, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Server running on port %d (debug: %v)\n", port, debugMode)
|
// Check loaded services from JSON file
|
||||||
fmt.Printf("Cache size: %d bytes\n", cacheSize)
|
if services, ok := data["services"].(map[string]interface{}); ok {
|
||||||
|
fmt.Printf("Loaded %d services from JSON config\n", len(services))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Environment variables are now available
|
||||||
|
fmt.Printf("DATABASE_URL env var: %s\n", os.Getenv("DATABASE_URL"))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example JSON files referenced above:
|
||||||
|
|
||||||
|
```json
|
||||||
|
// /etc/config/services.json
|
||||||
|
{
|
||||||
|
"production": {
|
||||||
|
"api": {
|
||||||
|
"endpoint": "https://api.example.com",
|
||||||
|
"timeout": 30,
|
||||||
|
"retries": 3
|
||||||
|
},
|
||||||
|
"cache": {
|
||||||
|
"provider": "redis",
|
||||||
|
"ttl": 3600
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"staging": {
|
||||||
|
"api": {
|
||||||
|
"endpoint": "https://api-staging.example.com",
|
||||||
|
"timeout": 60,
|
||||||
|
"retries": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /etc/config/features.json
|
||||||
|
{
|
||||||
|
"production": {
|
||||||
|
"new_ui": false,
|
||||||
|
"rate_limiting": true,
|
||||||
|
"analytics": true
|
||||||
|
},
|
||||||
|
"staging": {
|
||||||
|
"new_ui": true,
|
||||||
|
"rate_limiting": true,
|
||||||
|
"analytics": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user