Implements the six review comments on PR #1: - Per-host PXE-enable gate: read NetBox pxe_enabled custom field; a known host with it false gets the safe local-boot script (Cobbler netboot_enabled). Add a token-guarded POST /provisioned/{ident} callback that clears pxe_enabled in NetBox, plus a %post snippet in the default kickstarts that calls it. - Templates from a git repo: bootapi clones a templates repo and re-pulls every BOOTAPI_TEMPLATE_GIT_INTERVAL (default 3m), atomically swapping the template set (last-good kept on parse failure; embedded defaults are the startup fallback). Metrics for syncs/failures/generation. - Distro catalog (catalog/*.yaml): NetBox host -> boot images/kickstart, so adding an OS is a YAML + template change. Ships almalinux + fedora entries (artifactapi remotes); debian/talos path documented. - Boot images from the artifactapi almalinux/fedora remotes via the catalog. - Bind resolvers, puppet server/CA and PUPPETCA_URL env file now target the k8s services (198.18.200.7; puppet(ca).k8s.syd1.au.unkin.net). - Boot path served over plain HTTP (installers lack CA trust) with an optional parallel HTTPS listener; docs say do not 301 the boot endpoints. New packages: internal/catalog, internal/gitsync. NetBox client gains a pxe_enabled write (token needs that scope - noted in docs). `bootapi validate` subcommand validates a template/catalog set for the templates-repo CI. go build/vet clean, go test -race green, golangci-lint v2 clean, pre-commit clean. Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
4.3 KiB
bootapi
bootapi is a small Go service that replaces Cobbler's PXE/kickstart side. It
renders kickstart files and iPXE boot scripts from NetBox device data and
serves them over HTTP to PXE-booting hosts.
The ENC half of Cobbler already moved to encapi. With bootapi, provisioning a new host is:
terraformthe host into NetBox (device, interfaces/MACs, IP, platform, role) and encapi (classification), then- rack it and let it PXE-boot — DHCP points it at bootapi, which serves the iPXE script and the rendered kickstart; the kickstart hands off to the existing Puppet firstrun bootstrap.
How it works
DHCP next-server ─▶ iPXE ─▶ GET /ipxe/<mac> ─▶ boot kernel+initrd, inst.ks=<bootapi>/ks/<host>
└▶ GET /ks/<host> ─▶ rendered kickstart ─▶ puppet firstrun
bootapi identifies the booting host by the MAC it booted from (NetBox
interface lookup → device → primary IP, platform, role, interfaces), renders a
Go text/template selected from the host's platform/role/custom-field, and
serves it. Templates are embedded defaults, overridable from a directory
(ConfigMap in k8s).
Endpoints (summary)
| Path | Purpose |
|---|---|
GET /ipxe/{mac} · GET /boot/ipxe?mac= |
iPXE boot script |
GET /ks/{ident} |
rendered kickstart (MAC or hostname) |
POST /provisioned/{ident} |
end-of-kickstart callback (token) → clears pxe_enabled in NetBox |
GET /healthz · /readyz · /metrics |
health + Prometheus |
The boot path is served over plain HTTP (PXE installers have no internal-CA
trust); HTTPS is offered in parallel. Unknown MAC → iPXE gets a safe fallback
(local-disk boot, HTTP 200), never a 404; a host with pxe_enabled=false gets the
same fallback so it won't re-install. Unknown kickstart host → 404. Full
rationale in docs/endpoints.md.
Multi-distro + live templates
- Distro catalog (
catalog/*.yaml): each OS maps a NetBox platform/family to its boot images (artifactapi remotes), kernel args and kickstart template. Adding Fedora/Debian/Talos is a YAML + template change, no code change. Shipsalmalinux9+fedora. - Template git-sync: bootapi pulls the
bootapi-templatesrepo every 3m (like argocd) and hot-swaps the template set (last-good kept on a bad push); embedded defaults are the startup fallback.
Documentation
- docs/endpoints.md — the PXE flow, every endpoint, error/fallback behavior, metrics.
- docs/data-model.md — the exact template data model + NetBox custom fields + template selection.
- docs/template-authoring.md — writing/overriding kickstart & iPXE templates.
- docs/deployment.md — Kubernetes/argocd wiring, Vault secrets, and the DHCP cutover.
- docs/security.md — what belongs in NetBox vs Vault; secrets in kickstarts.
Configuration
Env-based (12-factor), see config.example.env. Key vars:
BOOTAPI_NETBOX_URL, BOOTAPI_NETBOX_TOKEN[_FILE], BOOTAPI_BASE_URL,
BOOTAPI_BOOT_BASE_URL, BOOTAPI_ROOT_PASSWORD_HASH[_FILE],
BOOTAPI_UNKNOWN_MAC_FALLBACK.
Development
make build # build ./bin/bootapi
make test-race # go test -race ./...
make lint # golangci-lint (v2) if installed, else go vet
make run # build + run
CI (Woodpecker): pre-commit, golangci-lint v2 + go test -race, and a
docker build on PRs; on a v* tag, a container image push and a Gitea binary
release. Cut a release with make patch|minor|major (tags + pushes).
Layout
cmd/bootapi/ main
internal/config/ env config
internal/model/ Host/Interface data model (incl. pxe_enabled gate)
internal/netbox/ NetBox client (reads + pxe_enabled write) + TTL cache, behind an interface
internal/catalog/ distro catalog: NetBox host -> boot images/kickstart
internal/render/ text/template engine (swappable Set), selection, loader
internal/gitsync/ periodic git pull + atomic template reload (last-good)
internal/server/ chi HTTP handlers + Prometheus metrics
templates/ embedded defaults: kickstart, iPXE, catalog/*.yaml
docs/ see above