diff --git a/README.md b/README.md index 67281bc..83c0119 100644 --- a/README.md +++ b/README.md @@ -40,33 +40,49 @@ go get git.eeqj.de/sneak/smartconfig ```yaml # config.yaml -# Example with top-level values for easy access +# Showcase the power of multiple resolver types app_name: ${ENV:APP_NAME} -port: ${ENV:PORT} # Becomes number 8080 if PORT="8080" -debug_mode: ${ENV:DEBUG_MODE} # Becomes boolean true if DEBUG_MODE="true" -cache_size_bytes: ${ENV:CACHE_SIZE} # Human-readable: "100MB", "1.5GB" +version: ${FILE:/app/VERSION} # Read from file +port: ${ENV:PORT} # Number if PORT="8080" +debug_mode: ${ENV:DEBUG_MODE} # Boolean if "true"/"false" -# Nested configuration for organization -server: - host: localhost - port: ${ENV:SERVER_PORT} - name: "server-${ENV:INSTANCE_ID}" # Always a string due to mixed content +# External service configuration from JSON config file +services: ${JSON:/etc/config/services.json:production} # Load entire object +# 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: - host: ${ENV:DB_HOST} - port: ${ENV:DB_PORT} + host: ${CONSUL:service/postgres/address} + port: ${CONSUL:service/postgres/port} + name: ${ENV:DB_NAME} 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: - cache_enabled: ${ENV:CACHE_ENABLED} # Boolean if "true"/"false" - max_connections: ${ENV:MAX_CONN} # Number if numeric string - debug: ${ENV:FEATURES_DEBUG} +# Feature flags from JSON file +features: ${JSON:/etc/config/features.json:${ENV:ENVIRONMENT}} + +# Server configuration +server: + listen: "0.0.0.0:${ENV:PORT}" + workers: ${EXEC:nproc} # Dynamic based on CPU cores + hostname: ${EXEC:hostname -f} env: - # These will be exported as environment variables - API_TOKEN: ${AWSSM:api-token} - SLACK_WEBHOOK: ${FILE:/etc/secrets/slack_webhook.txt} + # Export these as environment variables for child processes + DATABASE_URL: "postgres://${ENV:DB_USER}:${GCPSM:projects/myproject/secrets/db-password}@${CONSUL:service/postgres/address}:5432/${ENV:DB_NAME}" + NEW_RELIC_LICENSE: ${AWSSM:monitoring/newrelic-license} ``` ### Go Code @@ -77,6 +93,7 @@ package main import ( "fmt" "log" + "os" "git.eeqj.de/sneak/smartconfig" ) @@ -88,29 +105,77 @@ func main() { log.Fatal(err) } - // Access typed values (top-level keys only) - port, err := config.GetInt("port") - if err != nil { - log.Fatal(err) - } + // Access typed values + appName, _ := config.GetString("app_name") + port, _ := config.GetInt("port") + 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() - var debugMode bool - if features, ok := data["features"].(map[string]interface{}); ok { - if debug, ok := features["debug"].(bool); ok { - debugMode = debug + + // API keys configuration + if apiKeys, ok := data["api_keys"].(map[string]interface{}); ok { + if stripe, ok := apiKeys["stripe"].(string); ok { + fmt.Printf("Stripe API key loaded: %s...\n", stripe[:8]) } } - // Human-readable byte sizes (top-level) - cacheSize, err := config.GetBytes("cache_size_bytes") - if err != nil { - log.Fatal(err) + // Database configuration + if db, ok := data["database"].(map[string]interface{}); ok { + host, _ := db["host"].(string) + port, _ := db["port"].(int) + fmt.Printf("Database: %s:%d\n", host, port) } - fmt.Printf("Server running on port %d (debug: %v)\n", port, debugMode) - fmt.Printf("Cache size: %d bytes\n", cacheSize) + // Check loaded services from JSON file + 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 + } } ```