274c480b09
bootapi replaces Cobbler's PXE/kickstart side. It resolves a PXE-booting host
from NetBox (by MAC or hostname), renders an iPXE boot script and a kickstart
from Go text/templates, and serves them over HTTP. The ENC half already moved to
encapi; this covers the provisioning/boot half.
What's here:
- cmd/bootapi + internal/{config,model,netbox,render,server}; embedded default
templates under templates/ (AlmaLinux 9 + Fedora kickstarts, iPXE boot +
unknown-MAC fallbacks) ported from Cobbler's boot/bootstrap contract.
- NetBox client (v4.x API) behind a Resolver interface with a short-TTL cache;
tested against httptest fixtures using real NetBox JSON shapes.
- chi HTTP server: /ipxe/{mac}, /boot/ipxe?mac=, /ks/{ident}, healthz/readyz,
Prometheus /metrics. Unknown MAC -> safe fallback iPXE (200), unknown KS -> 404.
- Secrets (root pw hash, ssh keys) injected at render time from env/Vault, never
NetBox. Config is env-based per estate convention.
- Makefile (build/test/lint/docker + patch/minor/major), Dockerfile (distroless),
.woodpecker (pre-commit, golangci-lint v2 + go test -race, docker build on PR;
image push + Gitea binary release on v* tag), docs/ and example config.
go build/vet clean, go test -race green, golangci-lint v2 clean, pre-commit clean.
Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
78 lines
3.9 KiB
Markdown
78 lines
3.9 KiB
Markdown
# Template data model
|
|
|
|
Every kickstart and iPXE template is rendered with Go `text/template` against a
|
|
single flat value. This is the exact, stable contract template authors code
|
|
against. It is assembled in `internal/render.dataFor` from a NetBox device
|
|
(`internal/netbox`) plus render-time config (env/Vault).
|
|
|
|
## Kickstart templates (`*.ks.tmpl`)
|
|
|
|
| Field | Type | Source | Notes |
|
|
|-------|------|--------|-------|
|
|
| `.Hostname` | string | NetBox device name | short name, e.g. `web01` |
|
|
| `.Domain` | string | NetBox CF `domain`, else `BOOTAPI_DOMAIN` | |
|
|
| `.FQDN` | string | derived | `Hostname.Domain` (or `Hostname` if no domain) |
|
|
| `.Platform` | string | NetBox platform slug | e.g. `almalinux9` — primary template-selection key |
|
|
| `.OSFamily` | string | derived from platform | e.g. `almalinux` |
|
|
| `.OSVersion` | string | derived from platform | e.g. `9` |
|
|
| `.Arch` | string | fixed `x86_64` (today) | |
|
|
| `.Role` | string | NetBox device role slug | e.g. `kubernetes-worker` |
|
|
| `.Interfaces` | `[]Interface` | NetBox interfaces + IPs | primary interface sorted first |
|
|
| `.PrimaryInterface` | `*Interface` | derived | the NIC carrying the primary IP (or first) |
|
|
| `.PrimaryIP` | string | NetBox device `primary_ip` | address only, no prefix |
|
|
| `.Nameservers` | `[]string` | NetBox CF `nameservers`, else `BOOTAPI_NAMESERVERS` | |
|
|
| `.RootPasswordHash` | string | **render-time** (`BOOTAPI_ROOT_PASSWORD_HASH[_FILE]`) | crypt(3) hash; empty ⇒ lock root. **Never** from NetBox — see [security.md](security.md) |
|
|
| `.SSHAuthorizedKeys` | `[]string` | **render-time** (`BOOTAPI_SSH_AUTHORIZED_KEYS`) | |
|
|
| `.PuppetServer` | string | `BOOTAPI_PUPPET_SERVER` | default `puppet.query.consul` |
|
|
| `.PuppetCAServer` | string | `BOOTAPI_PUPPET_CA_SERVER` | default `puppetca.query.consul` |
|
|
| `.BaseURL` | string | `BOOTAPI_BASE_URL` | bootapi's own URL |
|
|
| `.BootBaseURL` | string | `BOOTAPI_BOOT_BASE_URL` | OS install-tree base |
|
|
| `.KickstartURL` | string | derived | `BaseURL/ks/Hostname` |
|
|
| `.Custom` | `map[string]any` | **all** NetBox custom fields, verbatim | escape hatch for site-specific knobs without a code change |
|
|
|
|
### `Interface`
|
|
|
|
| Field | Type | Notes |
|
|
|-------|------|-------|
|
|
| `.Name` | string | NetBox interface name, e.g. `eth0` |
|
|
| `.MAC` | string | normalized lower-case colon form |
|
|
| `.IP` | string | address only (empty ⇒ no IP; skip in the network stanza) |
|
|
| `.PrefixLen` | int | CIDR length, e.g. `24` |
|
|
| `.Netmask` | string | dotted-quad, e.g. `255.255.255.0` |
|
|
| `.Gateway` | string | per-IP CF `gateway`, else device CF `gateway`, else empty |
|
|
| `.VLAN` | int | untagged VLAN id, or 0 |
|
|
| `.Primary` | bool | true for the NIC with the primary IP |
|
|
|
|
## iPXE templates (`*.ipxe.tmpl`)
|
|
|
|
Rendered with everything above **plus**:
|
|
|
|
| Field | Type | Notes |
|
|
|-------|------|-------|
|
|
| `.KernelURL` | string | `BootBaseURL/images/pxeboot/vmlinuz` (empty if `BootBaseURL` unset) |
|
|
| `.InitrdURL` | string | `BootBaseURL/images/pxeboot/initrd.img` |
|
|
|
|
The fallback templates (`fallback-local`, `fallback-shell`) are rendered with an
|
|
empty value — they take no host data by design.
|
|
|
|
## NetBox custom fields bootapi reads
|
|
|
|
Define these on the *device* (or, where noted, the *IP address*) in NetBox.
|
|
All are optional; sensible fallbacks apply.
|
|
|
|
| Custom field | On | Effect |
|
|
|--------------|----|--------|
|
|
| `domain` | device | DNS domain; overrides `BOOTAPI_DOMAIN` |
|
|
| `gateway` | device / IP address | default gateway (IP-level wins) |
|
|
| `nameservers` | device | comma-separated resolvers; overrides `BOOTAPI_NAMESERVERS` |
|
|
| `provision_template` | device | force a specific template name (see below) |
|
|
|
|
## Template selection precedence
|
|
|
|
`SelectKickstart` picks the first template name that exists, in order:
|
|
|
|
1. `provision_template` custom field (exact template name)
|
|
2. `.Platform` slug (e.g. `almalinux9`)
|
|
3. `.OSFamily` (e.g. `almalinux`, or `fedora`)
|
|
4. `BOOTAPI_DEFAULT_TEMPLATE` (default `almalinux9`)
|