feat(frank-dragoon): open the NPU node the way rootless can actually reach it
The armed boot reached the engine and died in its own preflight:
halogen npu: /dev/accel/accel0 is here, but this process cannot open it
(crw-rw---- nobody:nogroup).
The node is root:render 0660 and a rootless container maps exactly one uid/gid,
so `render` is unmapped inside it and `--group-add keep-groups` — the vendor's
advice, and the ComfyUI pattern for /dev/kfd — cannot grant a group the
namespace does not map. Measured 2026-10-19 from a `dank` shell already in
`render`: without the flag denied, with the flag denied, with the udev mode
0666 the open succeeds (OPEN_OK). Same wall 99-comfyui-gpu-access.rules already
solved for the GPU compute nodes, so the accel node gets its counterpart:
dankclaw/udev/98-halogen-npu-access.rules — KERNEL=="accel[0-9]*",
SUBSYSTEM=="accel", MODE="0666"
The deployer installs the rule, reloads rules, re-triggers the node and only
then checks the mode; the serve script refuses any node that is not
other-writable, naming the rule, so this cannot return as a crash loop (189
restarts while chat was down is the failure mode being closed).
Both checks read the permission bit directly: `[ -o FILE ]` is not
"other-writable" — it means "exists" and `-o` doubles as the OR operator inside
test, which is why the first cut of the gate refused everything.
Dragoon seam: 33 -> 35 tests. The fixture node is 0666 like the installed one,
a 0660 node is refused with the rule named and nothing rendered, and the rule
file's shape plus the deployer's install/trigger are asserted.
This commit is contained in:
@@ -30,6 +30,7 @@ setup() {
|
||||
HOST_UNIT="$ROOT/dankclaw/host/halogen-fabric-clock"
|
||||
HOST_SERVICE="$ROOT/dankclaw/host/halogen-fabric-clock.service"
|
||||
NPU_FETCH="$ROOT/scripts/download-frank-dragoon-npu.sh"
|
||||
NPU_RULE="$ROOT/dankclaw/udev/98-halogen-npu-access.rules"
|
||||
BUILD="$ROOT/scripts/build-rocmfpx.sh"
|
||||
TEST_TMP="$(mktemp -d)"
|
||||
touch "$TEST_TMP/empty.env"
|
||||
@@ -418,7 +419,10 @@ $(cat "$TEST_TMP/serve.log")"
|
||||
# refused BY NAME instead of surfacing as an engine crash-loop.
|
||||
|
||||
npu_fixture() {
|
||||
# 0666 like the installed node: a rootless container's namespace cannot open
|
||||
# the stock root:render 0660 mode, and the serve gate checks exactly that.
|
||||
touch "$TEST_TMP/accel0"
|
||||
chmod 666 "$TEST_TMP/accel0"
|
||||
mkdir -p "$TEST_TMP/xrt"
|
||||
for l in libxrt_coreutil.so.2 libxrt_core.so.2 libxrt_driver_xdna.so.2; do
|
||||
touch "$TEST_TMP/xrt/$l"
|
||||
@@ -526,6 +530,32 @@ $(cat "$TEST_TMP/serve.log")"
|
||||
[[ "$output" == *"iommu=pt"* ]]
|
||||
}
|
||||
|
||||
@test "the lane refuses an NPU node the container namespace could not open, and names the rule" {
|
||||
# Stock Debian mode is root:render 0660. The engine's own first words on such
|
||||
# a node are "this process cannot open it (crw-rw---- nobody:nogroup)" and no
|
||||
# flag fixes a mode an unmapped gid cannot cross (keep-groups measured dead
|
||||
# 2026-10-19), so the unit refuses instead of crash-looping 189 times.
|
||||
podman_stub
|
||||
good_env
|
||||
npu_fixture
|
||||
chmod 660 "$TEST_TMP/accel0"
|
||||
run_with_log
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"$TEST_TMP/accel0 is 660"* ]]
|
||||
[[ "$output" == *"98-halogen-npu-access.rules"* ]]
|
||||
[[ "$output" != *"--device"* ]]
|
||||
}
|
||||
|
||||
@test "the NPU access rule is the accel counterpart of the GPU compute rule" {
|
||||
[ -f "$NPU_RULE" ]
|
||||
grep -qE '^KERNEL=="accel\[0-9\]\*", SUBSYSTEM=="accel", MODE="0666"$' "$NPU_RULE"
|
||||
# The deployer installs the rule and re-triggers the node BEFORE it trusts
|
||||
# the mode, so a machine that never had it self-heals instead of refusing.
|
||||
grep -qE '98-halogen-npu-access\.rules' "$DEPLOYER"
|
||||
grep -qE 'udevadm control --reload-rules' "$DEPLOYER"
|
||||
grep -qE 'udevadm trigger --sysname-match=' "$DEPLOYER"
|
||||
}
|
||||
|
||||
@test "the lane refuses when an XRT NPU library is missing" {
|
||||
podman_stub
|
||||
good_env
|
||||
|
||||
Reference in New Issue
Block a user