Documentation
¶
Overview ¶
Package config provides a simple, structured, and extensible way to manage application configuration in Go.
It builds upon the Viper library and adds powerful features like validation, default value registration, environment and flag integration, and structured config registration.
Key Features:
- Register typed configuration structs with default values.
- Parse YAML configuration files and bind fields to CLI flags and environment variables.
- Automatically generate flags based on struct field tags.
- Validate configuration using custom logic (via `Validate()` method).
- Write current configuration back to disk and trigger onChange handlers.
- Load configuration from disk on each Read() call.
- Automatically fallbacks to default config creation if no file is found.
All configuration structs must implement the `Config` interface:
type Config interface {
Validate() error
}
Example:
package config
import (
"fmt"
"github.com/valentin-kaiser/go-core/config"
"github.com/valentin-kaiser/go-core/flag"
)
type ServerConfig struct {
Host string `yaml:"host" usage:"The host of the server"`
Port int `yaml:"port" usage:"The port of the server"`
}
func (c *ServerConfig) Validate() error {
if c.Host == "" {
return fmt.Errorf("host cannot be empty")
}
if c.Port <= 0 {
return fmt.Errorf("port must be greater than 0")
}
return nil
}
func Get() *ServerConfig {
c, ok := config.Get().(*ServerConfig)
if !ok {
return &ServerConfig{}
}
return c
}
func init() {
cfg := &ServerConfig{
Host: "localhost",
Port: 8080,
}
// Register config - path parameter is ignored, flag.Path will be used
if err := config.Register("", "server", cfg); err != nil {
fmt.Println("Error registering config:", err)
return
}
// Parse flags (including --path and config-specific flags)
flag.Init()
// Read config using the parsed --path flag - loads from disk each time
if err := config.Read(); err != nil {
fmt.Println("Error reading config:", err)
return
}
// Register onChange handler to be called when Write() is used
config.OnChange(func(o, n config.Config) error {
fmt.Println("Configuration changed")
return nil
})
// Update configuration - this will trigger onChange handlers
if err := config.Write(cfg); err != nil {
fmt.Println("Error writing config:", err)
}
}
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNotFound = errors.New("configuration not found")
ErrNotFound is returned by a Source when the requested configuration does not yet exist in the backing store. When Read() encounters this from the base source, it bootstraps the source by saving the current default configuration and then loads it again.
var ErrReadOnly = errors.New("configuration source is read-only")
ErrReadOnly may be returned by a Source from Save to indicate that the underlying store does not support writes. Manager.Write treats this as a non-fatal condition for overlays but as fatal for the base source.
var ErrWatchUnsupported = errors.New("configuration source does not support watch")
ErrWatchUnsupported may be returned by a Source from Watch to indicate that the underlying store does not support change notifications. StartWatch silently skips sources that return this error.
Functions ¶
func Changed ¶
Changed checks if two configuration values are different by comparing their reflection values. It returns true if the configurations differ, false if they are the same. This function handles nil values correctly and performs deep comparison of the underlying values.
func Manager ¶ added in v1.5.3
func Manager() *manager
Manager returns the singleton configuration manager instance
func Read ¶
func Read() error
Read loads the configuration from the configured sources, validates it and applies it. The base source is consulted first; any registered overlays are then merged on top. When the base source reports that no configuration exists yet (ErrNotFound), the current in-memory defaults are persisted and the source is loaded again. Read always reloads from the sources; values are never cached across calls.
func Reset ¶
func Reset()
Reset clears the config package state Everything must be re-registered after calling this function
func StartWatch ¶ added in v1.10.0
StartWatch subscribes to change notifications from the base source and all overlays that support them. When any watched source reports a change, the configuration is reloaded and OnChange handlers are invoked. Sources that return ErrWatchUnsupported are silently skipped. Call StopWatch to stop watching. StartWatch is safe to call multiple times; previous watches are cancelled first.
Types ¶
type Config ¶
type Config interface {
Validate() error
}
Config is the interface that all configuration structs must implement It should contain a Validate method that checks the configuration for errors
type Source ¶ added in v1.10.0
type Source interface {
// Name returns a short, human readable identifier of the source used in
// log and error messages (e.g. "file", "etcd").
Name() string
// Load returns the full set of configuration values as a flat map keyed
// by lower-case dotted paths (e.g. "server.port"). Implementations must
// return ErrNotFound if the configuration has not been initialised yet.
Load(ctx context.Context) (map[string]interface{}, error)
// Save persists the given configuration. Implementations that are
// read-only must return ErrReadOnly.
Save(ctx context.Context, c Config) error
// Watch subscribes to changes in the source. The callback is invoked with
// the freshly loaded values on every change. The returned cancel function
// stops the watch. Implementations that do not support watching must
// return ErrWatchUnsupported.
Watch(ctx context.Context, onChange func(map[string]interface{})) (cancel func(), err error)
}
Source abstracts the backing store of a configuration. Built-in sources include a YAML file source (the default) and the etcd source provided by the etcd package. Custom sources may implement this interface to plug additional stores (Consul, Vault, HTTP endpoints, ...).
All methods are invoked from the manager goroutine and may be called concurrently with Watch callbacks; implementations must be safe for use by multiple goroutines.