168 lines
3.9 KiB
Go
168 lines
3.9 KiB
Go
// FILE: example/watch_demo.go
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/lixenwraith/config"
|
|
)
|
|
|
|
// AppConfig represents our application configuration
|
|
type AppConfig struct {
|
|
Server struct {
|
|
Host string `toml:"host"`
|
|
Port int `toml:"port"`
|
|
} `toml:"server"`
|
|
|
|
Database struct {
|
|
URL string `toml:"url"`
|
|
MaxConns int `toml:"max_conns"`
|
|
IdleTimeout time.Duration `toml:"idle_timeout"`
|
|
} `toml:"database"`
|
|
|
|
Features struct {
|
|
RateLimit bool `toml:"rate_limit"`
|
|
Caching bool `toml:"caching"`
|
|
} `toml:"features"`
|
|
}
|
|
|
|
func main() {
|
|
// Create configuration with defaults
|
|
defaults := &AppConfig{}
|
|
defaults.Server.Host = "localhost"
|
|
defaults.Server.Port = 8080
|
|
defaults.Database.MaxConns = 10
|
|
defaults.Database.IdleTimeout = 30 * time.Second
|
|
|
|
// Build configuration
|
|
cfg, err := config.NewBuilder().
|
|
WithDefaults(defaults).
|
|
WithEnvPrefix("MYAPP_").
|
|
WithFile("config.toml").
|
|
Build()
|
|
if err != nil && !errors.Is(err, config.ErrConfigNotFound) {
|
|
log.Fatal("Failed to load config:", err)
|
|
}
|
|
|
|
// Enable auto-update with custom options
|
|
watchOpts := config.WatchOptions{
|
|
PollInterval: 500 * time.Millisecond, // Check twice per second
|
|
Debounce: 200 * time.Millisecond, // Quick response
|
|
MaxWatchers: 10,
|
|
ReloadTimeout: 2 * time.Second,
|
|
VerifyPermissions: true, // SECURITY: Detect permission changes
|
|
}
|
|
cfg.AutoUpdateWithOptions(watchOpts)
|
|
defer cfg.StopAutoUpdate()
|
|
|
|
// Start watching for changes
|
|
changes := cfg.Watch()
|
|
|
|
// Context for graceful shutdown
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
// Handle signals
|
|
sigCh := make(chan os.Signal, 1)
|
|
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
// Log initial configuration
|
|
logConfig(cfg)
|
|
|
|
// Watch for changes
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case path := <-changes:
|
|
handleConfigChange(cfg, path)
|
|
}
|
|
}
|
|
}()
|
|
|
|
// Main loop
|
|
log.Println("Watching for configuration changes. Edit config.toml to see updates.")
|
|
log.Println("Press Ctrl+C to exit.")
|
|
|
|
ticker := time.NewTicker(10 * time.Second)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-sigCh:
|
|
log.Println("Shutting down...")
|
|
return
|
|
|
|
case <-ticker.C:
|
|
// Periodic health check
|
|
var port int64
|
|
port, _ = cfg.Get("server.port")
|
|
log.Printf("Server still running on port %d", port)
|
|
}
|
|
}
|
|
}
|
|
|
|
func handleConfigChange(cfg *config.Config, path string) {
|
|
switch path {
|
|
case "__file_deleted__":
|
|
log.Println("⚠️ Config file was deleted!")
|
|
case "__permissions_changed__":
|
|
log.Println("⚠️ SECURITY: Config file permissions changed!")
|
|
case "__reload_error__":
|
|
log.Printf("❌ Failed to reload config: %s", path)
|
|
case "__reload_timeout__":
|
|
log.Println("⚠️ Config reload timed out")
|
|
default:
|
|
// Normal configuration change
|
|
value, _ := cfg.Get(path)
|
|
log.Printf("📝 Config changed: %s = %v", path, value)
|
|
|
|
// Handle specific changes
|
|
switch path {
|
|
case "server.port":
|
|
log.Println("Port changed - server restart required")
|
|
case "database.url":
|
|
log.Println("Database URL changed - reconnection required")
|
|
case "features.rate_limit":
|
|
if cfg.Bool("features.rate_limit") {
|
|
log.Println("Rate limiting enabled")
|
|
} else {
|
|
log.Println("Rate limiting disabled")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func logConfig(cfg *config.Config) {
|
|
log.Println("Current configuration:")
|
|
log.Printf(" Server: %s:%d", cfg.String("server.host"), cfg.Int("server.port"))
|
|
log.Printf(" Database: %s (max_conns=%d)",
|
|
cfg.String("database.url"),
|
|
cfg.Int("database.max_conns"))
|
|
log.Printf(" Features: rate_limit=%v, caching=%v",
|
|
cfg.Bool("features.rate_limit"),
|
|
cfg.Bool("features.caching"))
|
|
}
|
|
|
|
// Example config.toml file:
|
|
/*
|
|
[server]
|
|
host = "localhost"
|
|
port = 8080
|
|
|
|
[database]
|
|
url = "postgres://localhost/myapp"
|
|
max_conns = 25
|
|
idle_timeout = "30s"
|
|
|
|
[features]
|
|
rate_limit = true
|
|
caching = false
|
|
*/ |