watchdog: migrate to Cobra and Viper for config management; search /etc for configs
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I65dbf466cb030dccc7025585d6282bd26a6a6964
This commit is contained in:
parent
951ed9c36f
commit
f988174145
9 changed files with 187 additions and 61 deletions
|
|
@ -1,15 +1,92 @@
|
|||
package watchdog
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"notashelf.dev/watchdog/internal/config"
|
||||
)
|
||||
|
||||
func Main() {
|
||||
configPath := flag.String("config", "config.yaml", "path to config file")
|
||||
flag.Parse()
|
||||
var (
|
||||
cfgFile string
|
||||
cfg *config.Config
|
||||
)
|
||||
|
||||
if err := Run(*configPath); err != nil {
|
||||
log.Fatalf("Error: %v", err)
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "watchdog",
|
||||
Short: "Privacy-first web analytics with Prometheus metrics",
|
||||
Long: `Watchdog is a lightweight, privacy-first analytics system that aggregates web traffic data.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return Run(cfg)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
rootCmd.PersistentFlags().
|
||||
StringVar(&cfgFile, "config", "", "config file (default: config.yaml)")
|
||||
rootCmd.PersistentFlags().String("listen-addr", "", "server listen address (overrides config)")
|
||||
rootCmd.PersistentFlags().String("metrics-path", "", "metrics endpoint path (overrides config)")
|
||||
rootCmd.PersistentFlags().
|
||||
String("ingestion-path", "", "ingestion endpoint path (overrides config)")
|
||||
|
||||
// Bind flags to viper
|
||||
viper.BindPFlag("server.listen_addr", rootCmd.PersistentFlags().Lookup("listen-addr"))
|
||||
viper.BindPFlag("server.metrics_path", rootCmd.PersistentFlags().Lookup("metrics-path"))
|
||||
viper.BindPFlag("server.ingestion_path", rootCmd.PersistentFlags().Lookup("ingestion-path"))
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
if cfgFile != "" {
|
||||
// Use config file from the flag
|
||||
viper.SetConfigFile(cfgFile)
|
||||
} else {
|
||||
// Search for config in current directory
|
||||
viper.AddConfigPath(".")
|
||||
viper.AddConfigPath("/etc/watchdog")
|
||||
viper.SetConfigName("config")
|
||||
viper.SetConfigType("yaml")
|
||||
}
|
||||
|
||||
// Environment variables
|
||||
viper.SetEnvPrefix("WATCHDOG")
|
||||
viper.AutomaticEnv()
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
|
||||
// Read config file
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||
fmt.Fprintf(
|
||||
os.Stderr,
|
||||
"Config file not found, using defaults and environment variables\n",
|
||||
)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Error reading config file: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Unmarshal into config struct
|
||||
cfg = &config.Config{}
|
||||
if err := viper.Unmarshal(cfg); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to parse config: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Validate config
|
||||
if err := cfg.Validate(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Config validation failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func Main() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,13 +21,7 @@ import (
|
|||
"notashelf.dev/watchdog/internal/normalize"
|
||||
)
|
||||
|
||||
func Run(configPath string) error {
|
||||
// Load configuration
|
||||
cfg, err := config.Load(configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load config: %w", err)
|
||||
}
|
||||
|
||||
func Run(cfg *config.Config) error {
|
||||
log.Printf("Loaded config for domains: %v", cfg.Site.Domains)
|
||||
|
||||
// Initialize components
|
||||
|
|
@ -35,7 +29,7 @@ func Run(configPath string) error {
|
|||
pathRegistry := aggregate.NewPathRegistry(cfg.Limits.MaxPaths)
|
||||
refRegistry := normalize.NewReferrerRegistry(cfg.Limits.MaxSources)
|
||||
eventRegistry := aggregate.NewCustomEventRegistry(cfg.Limits.MaxCustomEvents)
|
||||
metricsAgg := aggregate.NewMetricsAggregator(pathRegistry, eventRegistry, *cfg)
|
||||
metricsAgg := aggregate.NewMetricsAggregator(pathRegistry, eventRegistry, cfg)
|
||||
|
||||
// HLL state persistence is handled automatically if salt_rotation is configured
|
||||
if cfg.Site.SaltRotation != "" {
|
||||
|
|
@ -48,7 +42,7 @@ func Run(configPath string) error {
|
|||
|
||||
// Create HTTP handlers
|
||||
ingestionHandler := api.NewIngestionHandler(
|
||||
*cfg,
|
||||
cfg,
|
||||
pathNormalizer,
|
||||
pathRegistry,
|
||||
refRegistry,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue