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:
Jeffrey Paul 2025-07-21 16:21:33 +02:00
parent 81eddf2b38
commit e6db26d2c4

135
README.md
View File

@ -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}
features:
cache_enabled: ${ENV:CACHE_ENABLED} # Boolean if "true"/"false"
max_connections: ${ENV:MAX_CONN} # Number if numeric string
debug: ${ENV:FEATURES_DEBUG}
# 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}
# 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
}
}
```