// Command validator-agent runs on a validator VM. It is stateless: every // decision it makes is driven by polling the Control API, per the // deployment constraint that validators must be able to restart freely // without any local state to reconcile. package main import ( "context" "flag" "log/slog" "net" "os" "os/signal" "strconv" "strings" "syscall" "cloudipvalidator/internal/agentcore" "cloudipvalidator/internal/config" ) func main() { configPath := flag.String("config", "configs/validator-agent.yaml", "path to validator-agent config file") stubPorts := flag.String("stub-ports", "", "TEST ONLY: comma-separated TCP ports to accept-and-close on, standing in for the validator's real listening services in the offline end-to-end harness (see docs/LOCAL_E2E.md)") flag.Parse() log := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})) cfg, err := config.LoadValidatorAgent(*configPath) if err != nil { log.Error("load config", "err", err) os.Exit(1) } ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() if *stubPorts != "" { startStubListeners(ctx, log, *stubPorts) } token := os.Getenv(cfg.ControlAPITokenEnv) if token == "" { log.Warn("agent token is not set: result/event/complete calls will be rejected by an authenticated control-api", "env", cfg.ControlAPITokenEnv) } agent := agentcore.New(cfg, log).WithToken(token) if err := agent.Run(ctx); err != nil && err != context.Canceled { log.Error("agent stopped", "err", err) os.Exit(1) } } // startStubListeners binds trivial accept-and-close TCP listeners on the // given ports, standing in for the base-minimum services (22/80/443/8080) // a real validator would already run, so an offline prober has something // to successfully connect to. ICMP needs no stub: the kernel answers echo // requests to any locally-bound address on its own. func startStubListeners(ctx context.Context, log *slog.Logger, portsCSV string) { for _, p := range strings.Split(portsCSV, ",") { p = strings.TrimSpace(p) if p == "" { continue } port, err := strconv.Atoi(p) if err != nil { log.Error("invalid stub port", "value", p, "err", err) continue } ln, err := net.Listen("tcp", ":"+strconv.Itoa(port)) if err != nil { log.Error("stub listener", "port", port, "err", err) continue } log.Info("stub listener up", "port", port) go func() { <-ctx.Done() ln.Close() }() go func() { for { conn, err := ln.Accept() if err != nil { return } conn.Close() } }() } }