diff --git a/cmd/vetting/main.go b/cmd/vetting/main.go index 5633fac..c51914a 100644 --- a/cmd/vetting/main.go +++ b/cmd/vetting/main.go @@ -157,6 +157,7 @@ func main() { TFTPRoot: tftpRoot, LiveDir: cfg.PXE.LiveDir, }) + ui.PXE = supervisor } router := httpserver.NewRouter(httpserver.Deps{ diff --git a/internal/api/ui_handlers.go b/internal/api/ui_handlers.go index aff370e..3b3cc91 100644 --- a/internal/api/ui_handlers.go +++ b/internal/api/ui_handlers.go @@ -35,6 +35,34 @@ type UI struct { Runner *orchestrator.Runner Tiles *TileEnricher PublicURL string // user-visible base URL baked into the quick-register one-liner + // PXE, when non-nil, gets Reload()ed after host create/delete so + // dnsmasq's dhcp-host= allowlist reflects the current registry. + // Without this, a newly-registered host PXE-boots and gets + // "proxy-ignored" because its MAC isn't tagged `known`. + PXE PXEReloader +} + +// PXEReloader rewrites dnsmasq.conf with the current host list and +// SIGHUPs the subprocess. Satisfied by *pxe.Supervisor. +type PXEReloader interface { + Reload(hosts []model.Host) error +} + +// reloadPXE reads the full host list and hands it to the reloader. +// Logs on failure; never returns an error — the HTTP request that +// triggered the host change has already succeeded. +func (u *UI) reloadPXE(ctx context.Context) { + if u.PXE == nil { + return + } + hosts, err := u.Hosts.List(ctx) + if err != nil { + log.Printf("pxe reload: list hosts: %v", err) + return + } + if err := u.PXE.Reload(hosts); err != nil { + log.Printf("pxe reload: %v", err) + } } var macRe = regexp.MustCompile(`^[0-9a-f]{2}(:[0-9a-f]{2}){5}$`) @@ -246,6 +274,7 @@ func (u *UI) CreateHost(w http.ResponseWriter, r *http.Request) { _ = templates.Registration(form).Render(r.Context(), w) return } + u.reloadPXE(r.Context()) http.Redirect(w, r, "/", http.StatusSeeOther) } @@ -301,6 +330,7 @@ func (u *UI) CreateHostJSON(w http.ResponseWriter, r *http.Request) { return } log.Printf("api: registered host %d (%s, %s)", id, form.Name, form.MAC) + u.reloadPXE(r.Context()) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) _ = json.NewEncoder(w).Encode(map[string]any{ @@ -456,6 +486,7 @@ func (u *UI) DeleteHost(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusInternalServerError) return } + u.reloadPXE(r.Context()) http.Redirect(w, r, "/", http.StatusSeeOther) }