// Package configorch fans Render() out across every per-service // renderer in a stable order (haproxy → nftables → squid → // wireguard → unbound). The orchestrator stops on the first hard // error; ErrNotImplemented stubs are logged but do NOT abort the // run (squid/wireguard/unbound are stubs in Phase 2). package configorch import ( "context" "errors" "fmt" "strings" "git.netcell-it.de/projekte/edgeguard-native/internal/configgen" ) type Result struct { Name string Skipped bool Err error DurationS float64 // populated by callers if they care; orchestrator leaves it 0 } func (r Result) Status() string { switch { case r.Err == nil: return "ok" case errors.Is(r.Err, configgen.ErrNotImplemented): return "skipped (stub)" default: return "error: " + r.Err.Error() } } // Run invokes Render on every generator in `gens`, returning a per- // generator Result slice. Stops on the first hard error so a broken // HAProxy config doesn't bleed into a partial nftables rewrite. // // only: optional whitelist of generator names — empty slice means // "all". Useful for `render-config --only=haproxy` debugging. func Run(ctx context.Context, gens []configgen.Generator, only []string) ([]Result, error) { whitelist := map[string]bool{} for _, n := range only { whitelist[n] = true } out := make([]Result, 0, len(gens)) for _, g := range gens { if len(whitelist) > 0 && !whitelist[g.Name()] { out = append(out, Result{Name: g.Name(), Skipped: true}) continue } err := g.Render(ctx) out = append(out, Result{Name: g.Name(), Err: err}) if err != nil && !errors.Is(err, configgen.ErrNotImplemented) { // hard failure — surface it but return what's done so far return out, fmt.Errorf("%s: %w", g.Name(), err) } } return out, nil } // Summarise turns the result slice into a human-readable multiline // string. Used by `edgeguard-ctl render-config` to print to stdout. func Summarise(results []Result) string { var b strings.Builder for _, r := range results { if r.Skipped { fmt.Fprintf(&b, " %-10s skipped (filtered)\n", r.Name) continue } fmt.Fprintf(&b, " %-10s %s\n", r.Name, r.Status()) } return b.String() }