a414918350
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
96 lines
4.3 KiB
Markdown
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
|
|
```
|