// Package config provides a simple, clean, and generic configuration management system // that supports multiple environments and automatic value resolution. // // Features: // - Simple API: Just config.Get() and config.GetSecret() // - Environment Support: Separate configs for different environments (dev/prod/staging/etc) // - Value Resolution: Automatic resolution of special values: // - $ENV:VARIABLE - Read from environment variable // - $GSM:secret-name - Read from Google Secret Manager // - $ASM:secret-name - Read from AWS Secrets Manager // - $FILE:/path/to/file - Read from file contents // - Hierarchical Defaults: Environment-specific values override defaults // - YAML-based: Easy to read and edit configuration files // - Zero Dependencies: Only depends on yaml and cloud provider SDKs (optional) // // Usage: // // import "sneak.berlin/go/webhooker/pkg/config" // // // Set the environment explicitly // config.SetEnvironment("prod") // // // Get configuration values // baseURL := config.Get("baseURL") // apiTimeout := config.GetInt("timeout", 30) // // // Get secret values // apiKey := config.GetSecret("api_key") // dbPassword := config.GetSecret("db_password", "default") package config import ( "sync" "github.com/spf13/afero" ) // Global configuration manager instance var ( globalManager *Manager mu sync.Mutex // Protect global manager updates ) // getManager returns the global configuration manager, creating it if necessary func getManager() *Manager { mu.Lock() defer mu.Unlock() if globalManager == nil { globalManager = NewManager() } return globalManager } // SetEnvironment sets the active environment. func SetEnvironment(environment string) { getManager().SetEnvironment(environment) } // SetFs sets the filesystem to use for all file operations. // This is primarily useful for testing with an in-memory filesystem. func SetFs(fs afero.Fs) { mu.Lock() defer mu.Unlock() // Create a new manager with the specified filesystem newManager := NewManager() newManager.SetFs(fs) // Replace the global manager globalManager = newManager } // Get retrieves a configuration value. // // This looks for values in the following order: // 1. Environment-specific config (environments..config.) // 2. Config defaults (configDefaults.) // // Values are resolved if they contain special prefixes: // - $ENV:VARIABLE_NAME - reads from environment variable // - $GSM:secret-name - reads from Google Secret Manager // - $ASM:secret-name - reads from AWS Secrets Manager // - $FILE:/path/to/file - reads from file func Get(key string, defaultValue ...interface{}) interface{} { var def interface{} if len(defaultValue) > 0 { def = defaultValue[0] } return getManager().Get(key, def) } // GetString retrieves a configuration value as a string. func GetString(key string, defaultValue ...string) string { var def string if len(defaultValue) > 0 { def = defaultValue[0] } val := Get(key, def) if s, ok := val.(string); ok { return s } return def } // GetInt retrieves a configuration value as an integer. func GetInt(key string, defaultValue ...int) int { var def int if len(defaultValue) > 0 { def = defaultValue[0] } val := Get(key, def) switch v := val.(type) { case int: return v case int64: return int(v) case float64: return int(v) default: return def } } // GetBool retrieves a configuration value as a boolean. func GetBool(key string, defaultValue ...bool) bool { var def bool if len(defaultValue) > 0 { def = defaultValue[0] } val := Get(key, def) if b, ok := val.(bool); ok { return b } return def } // GetSecret retrieves a secret value. // // This looks for secrets defined in environments..secrets. func GetSecret(key string, defaultValue ...interface{}) interface{} { var def interface{} if len(defaultValue) > 0 { def = defaultValue[0] } return getManager().GetSecret(key, def) } // GetSecretString retrieves a secret value as a string. func GetSecretString(key string, defaultValue ...string) string { var def string if len(defaultValue) > 0 { def = defaultValue[0] } val := GetSecret(key, def) if s, ok := val.(string); ok { return s } return def } // Reload reloads the configuration from file. func Reload() error { return getManager().Reload() } // GetAllConfig returns all configuration values for the current environment. func GetAllConfig() map[string]interface{} { return getManager().GetAllConfig() } // GetAllSecrets returns all secrets for the current environment. func GetAllSecrets() map[string]interface{} { return getManager().GetAllSecrets() } // LoadFile loads configuration from a specific file. func LoadFile(configFile string) error { return getManager().LoadFile(configFile) }