v0.4.69: PNG boot-menu background (iPXE built from source), NFS AUTH_SYS, FleetDM logo
Three things, headlined by the long-blocked graphical PXE menu.
## 1. Graphical PXE boot background — the iVentoy feature, finally
iVentoy paints a PNG background on the PXE screen using stock iPXE
built with CONSOLE_FRAMEBUFFER + IMAGE_PNG + CONSOLE_CMD; the public
iPXE binaries omit those, so `console --picture` is a no-op on them.
We now build our own iPXE from upstream with that thin config delta
(deploy/ipxe/local/{general,console}.h).
The 8-release blocker was cc1 segfaulting when an amd64 gcc ran under
QEMU emulation on the arm64 build host. Fix: a new `ipxe-build`
Dockerfile stage pinned to $BUILDPLATFORM (native arch — no emulation)
that cross-compiles x86_64 iPXE with CROSS_COMPILE=x86_64-linux-gnu-.
The compiler runs native and emits x86_64. Validated end-to-end:
png.o + fbcon.o + pixbuf.o all compile and link (confirmed via the
linked-ELF symbol table, not just strings), ~112s, no segfault. Host
tools needed libc6-dev (dropped by --no-install-recommends; without
it the native host compile falls through to iPXE's freestanding
headers and dies on bits/stdint.h — fixed).
Server side:
- pxe_logo.rs is now a full-screen background compositor: a dark field
(matching the WebUI theme) with the operator's uploaded logo across
the top, or — with no upload — a default OpenPXE rainbow disc drawn
with pure pixel math (no font/SVG deps). Always 1024x768 (iPXE
doesn't scale; this is the universal mode). WebP/JPEG/GIF/PNG in,
PNG out (iPXE only eats PNG).
- /branding/pxe-logo always returns a PNG now (default when no logo,
default when SVG) so the menu always has a background.
- render_menu uses `console --picture … --top 290 || console`: paints
the background and reserves the logo band on PNG-capable binaries
(x86_64 UEFI), cleanly falls back to text on the others. The ASCII
wordmark is GONE.
Only x86_64 UEFI is built from source (host-arch-agnostic cross build);
BIOS/i386/arm64 keep upstream-fetched no-PNG binaries + text fallback.
Modern clients are overwhelmingly x86_64 UEFI.
## 2. NFS AUTH_SYS credential — fixes NFS3ERR_ACCES
v0.4.68's privileged-port fix got past MNT3ERR_ACCES (mount); operators
then hit NFS3ERR_ACCES on READDIR because nfs3_client defaults to
AUTH_NONE and virtually every server exports sec=sys. We now present an
AUTH_UNIX credential (uid 0 / gid 0): no_root_squash servers treat us
as root, root_squash servers map us to anon which reads any
world-readable ISO share. Kept fixed (no UI knob) to stay dead-simple.
Hint updated: a remaining NFS3ERR_ACCES is now a server-side
permission/squash issue, not IP/auth-flavor.
## 3. FleetDM-style full-width logo (top-left)
When a custom logo is uploaded the sidebar header drops the bundled
mark + "OpenPXE" wordmark and lets the logo span the header
(left-aligned, capped 200x50, contain). Rendered server-side via a
brand-class in index_html (has_custom_logo) so there's no flash of the
default. The bundled-default case is unchanged.
Tests: 164 passing. clippy -D warnings clean. iPXE build stage
validated in isolation before the full image build.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
2f12a2ae84
commit
1eb41288c3
@@ -1832,13 +1832,39 @@ async fn docs_lists_new_v0_4_5_endpoints() {
|
||||
// ─── v0.4.6: PXE logo endpoint ────────────────────────────────────────────
|
||||
|
||||
#[tokio::test]
|
||||
async fn pxe_logo_404_when_no_custom_logo_configured() {
|
||||
async fn pxe_background_serves_default_when_no_custom_logo() {
|
||||
// v0.4.69: the endpoint always returns a full-screen PNG background
|
||||
// now — when no custom logo is configured it composes the default
|
||||
// OpenPXE mark on a dark field rather than 404ing. This is what lets
|
||||
// the PXE menu's `console --picture` paint a real background instead
|
||||
// of falling back to the (now-removed) ASCII placeholder.
|
||||
let (state, _dir) = build_state().await;
|
||||
let app = build_router(state);
|
||||
let (s, body) = get(&app, "/branding/pxe-logo").await;
|
||||
assert_eq!(s, StatusCode::NOT_FOUND);
|
||||
let text = std::str::from_utf8(&body).unwrap();
|
||||
assert!(text.contains("no custom logo"), "got: {text}");
|
||||
let res = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.uri("/branding/pxe-logo")
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
res.headers()
|
||||
.get(axum::http::header::CONTENT_TYPE)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"image/png"
|
||||
);
|
||||
let body = axum::body::to_bytes(res.into_body(), usize::MAX)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(body.starts_with(b"\x89PNG"), "default background not a PNG");
|
||||
let width = u32::from_be_bytes([body[16], body[17], body[18], body[19]]);
|
||||
assert_eq!(width, 1024, "default background should be 1024 wide");
|
||||
}
|
||||
|
||||
/// Build a tiny valid PNG via the `image` crate. The v0.4.61 PXE-logo
|
||||
@@ -1857,10 +1883,11 @@ fn tiny_png() -> Vec<u8> {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn pxe_logo_404_when_uploaded_logo_is_svg() {
|
||||
// iPXE can't rasterize SVG, so an SVG upload deliberately doesn't
|
||||
// light up the PXE menu's `console --picture` overlay — the menu
|
||||
// simply paints without a logo.
|
||||
async fn pxe_background_falls_back_to_default_for_svg_upload() {
|
||||
// iPXE can't rasterize SVG, so an SVG upload doesn't paint as the
|
||||
// PXE background — but v0.4.69 still returns the *default* OpenPXE
|
||||
// background PNG (not a 404) so the boot screen stays graphical.
|
||||
// The WebUI top-left continues to render the SVG natively.
|
||||
let (state, _dir) = build_state().await;
|
||||
state
|
||||
.branding
|
||||
@@ -1871,10 +1898,23 @@ async fn pxe_logo_404_when_uploaded_logo_is_svg() {
|
||||
)
|
||||
.unwrap();
|
||||
let app = build_router(state);
|
||||
let (s, body) = get(&app, "/branding/pxe-logo").await;
|
||||
assert_eq!(s, StatusCode::NOT_FOUND);
|
||||
let text = std::str::from_utf8(&body).unwrap();
|
||||
assert!(text.contains("SVG"), "got: {text}");
|
||||
let res = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.uri("/branding/pxe-logo")
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
let body = axum::body::to_bytes(res.into_body(), usize::MAX)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(body.starts_with(b"\x89PNG"), "should serve default PNG for SVG");
|
||||
let width = u32::from_be_bytes([body[16], body[17], body[18], body[19]]);
|
||||
assert_eq!(width, 1024);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
Reference in New Issue
Block a user