Files
bootapi/README.md
T
unkin-agent a414918350
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
Fetch templates over HTTP instead of shelling out to git
The runtime image is distroless and has no git binary, so every sync
failed and bootapi silently served the stale embedded templates.

- fetch the branch tarball (<repo>/archive/<branch>.tar.gz) and extract
  it into an in-memory FS; no checkout, no writable volume
- digest the extracted tree, not the archive bytes, so a recompressed
  identical archive is not a change
- skip entries that would escape the tree
- log the source commit from Gitea's immutable Link header
2026-09-26 18:59:07 +10:00

96 lines
4.3 KiB
Markdown

# 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](https://git.unkin.net/unkin/encapi).
With bootapi, provisioning a new host is:
1. `terraform` the host into **NetBox** (device, interfaces/MACs, IP, platform,
role) and **encapi** (classification), then
2. 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](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. Ships
`almalinux9` + `fedora`.
- **Template git-sync**: bootapi pulls the `bootapi-templates` repo 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](docs/endpoints.md) — the PXE flow, every endpoint, error/fallback behavior, metrics.
- [docs/data-model.md](docs/data-model.md) — the exact template data model + NetBox custom fields + template selection.
- [docs/template-authoring.md](docs/template-authoring.md) — writing/overriding kickstart & iPXE templates.
- [docs/deployment.md](docs/deployment.md) — Kubernetes/argocd wiring, Vault secrets, and the DHCP cutover.
- [docs/security.md](docs/security.md) — what belongs in NetBox vs Vault; secrets in kickstarts.
## Configuration
Env-based (12-factor), see [`config.example.env`](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
```bash
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 templates-tarball fetch + atomic reload (last-good)
internal/server/ chi HTTP handlers + Prometheus metrics
templates/ embedded defaults: kickstart, iPXE, catalog/*.yaml
docs/ see above
```