package main import ( "context" "log" "os" "os/signal" "syscall" "time" "firewall_containers/network-go/config" "firewall_containers/network-go/docker" "firewall_containers/network-go/firewall" "firewall_containers/network-go/iptables" "firewall_containers/network-go/watcher" ) // Config path - can be overridden via environment variable const defaultConfigPath = "/etc/user/config/networks.json" // Watch period - can be overridden via environment variable const defaultWatchPeriod = 30 * time.Second func getConfigPath() string { path := os.Getenv("NETWORKS_CONFIG_PATH") if path == "" { return defaultConfigPath } return path } func getWatchPeriod() time.Duration { periodStr := os.Getenv("WATCH_PERIOD_SECONDS") if periodStr == "" { return defaultWatchPeriod } seconds, err := time.ParseDuration(periodStr + "s") if err != nil { log.Printf("MAIN: invalid WATCH_PERIOD_SECONDS=%s, using default %s", periodStr, defaultWatchPeriod) return defaultWatchPeriod } return seconds } func getDebug() bool { return os.Getenv("DEBUG") == "1" || os.Getenv("DEBUG") == "true" } func main() { log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) log.Println("MAIN: starting network-go firewall container manager") configPath := getConfigPath() watchPeriod := getWatchPeriod() debug := getDebug() log.Printf("MAIN: config path = %s", configPath) log.Printf("MAIN: watch period = %s", watchPeriod) log.Printf("MAIN: debug = %v", debug) // Create Docker client (uses DOCKER_HOST env var automatically) dockerClient, err := docker.NewClient() if err != nil { log.Fatalf("MAIN: failed to create Docker client: %v", err) } defer dockerClient.Close() // Create iptables manager iptablesMgr := iptables.NewManager(debug) ctx := context.Background() // Load initial config cfg, err := config.Load(configPath) if err != nil { log.Fatalf("MAIN: failed to load initial config: %v", err) } // Create the firewall orchestrator (needs config for resolver) orchestrator := firewall.NewOrchestrator(dockerClient, iptablesMgr, cfg) // Run full reconciliation orchestrator.ReconcileAll(ctx, cfg) // Set up file watcher to detect changes and re-reconcile onChange := func() { log.Println("MAIN: config file change detected, reloading and reconciling") newCfg, err := config.Load(configPath) if err != nil { log.Printf("MAIN: failed to reload config: %v", err) return } orchestrator.ReconcileAll(ctx, newCfg) } fileWatcher := watcher.NewFileWatcher(configPath, watchPeriod, onChange) fileWatcher.Start() // Wait for shutdown signal sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) sig := <-sigCh log.Printf("MAIN: received signal %v, shutting down", sig) fileWatcher.Stop() log.Println("MAIN: shutdown complete") }