Files
mesh-catalog/modules/mesh-build-image/README.md
T
jschoubben d13f74df40 A node that cannot open /dev/net/tun takes no image build, and every node loads tun at boot and keeps its running kernel's modules
An image build node up 24 days had lost its running kernel's modules to a linux-lts upgrade, so tun
could not load and every RUN line failed in pasta. mesh-build-image now proves the tun device and,
whatever its look refuses, judges its node not eligible (capacity 0 with the reason), so the asks go
to a node that can build them. The kernel module loads tun at boot and declares kernel-modules-hook,
which keeps the running kernel's modules across an upgrade. Needs mesh-sdk fix/ace-tun (Puller.Eligible).
2026-10-28 12:44:58 +01:00

55 lines
4.6 KiB
Markdown

# mesh-build-image — the image build module
Design 47, release R4c-9 (novox/hq ADR 0487, ADR 0466 §1, ADR 0469; issue 1703). The module that builds a module's
`image` artifact from the Dockerfile in the module's own directory, on a build machine, as an account of its own
(`runs-as`, no login shell, home at mode 0700). It serves every module's image, whatever the base, so it is not a
build module for one module. It claims no seat: it is assigned to the build machines and runs dark until the builder
uses it.
## Rootless
The tool is `buildah`, which the `buildah` module installs and provides as `rootless-image-build` with the machine's
reach: a machine without it refuses this module at assignment, naming `buildah`. The build runs as the module's own
account in a user namespace (`--isolation oci`), with buildah's image store and runtime state in
`/var/lib/mesh-build-image-store` (outside the home), no mount of the host, no container runtime socket, no registry
credential and an environment built from scratch.
The account's subordinate user and group ranges are not declared anywhere: the node-engine makes every `user` account
with `useradd`, which allocates them. The module's health reads `/etc/subuid` and `/etc/subgid` and is unhealthy
without a range for the account, without buildah, when a user namespace cannot be made, or when `/dev/net/tun`
cannot be opened (buildah makes a RUN line's network with pasta, which needs the tun driver; on 2026-10-28 a build node had the
device node and no driver, so every image build there failed with "Failed to open() /dev/net/tun: No such device"). The
kernel module loads `tun` at boot. Whatever the health refuses, the node is **not eligible**: the seat's puller judges
by a capacity of 0 with that reason (`capacity.<node>`, which `build_usage` shows), so the asks go to a node that can
build them; a look that passes again wakes the puller.
The Dockerfile's steps may use the network (ADR 0487 §6); nothing a step can read holds a key.
## Steps
- `build_image_fetch` fetches a commit over SSH into a workspace (a base commit makes the merge preview, as the other
build modules' fetch). Long: answers a step id and ends on its key in `steps`.
- `build_image_build` builds one image artifact: `buildah bud` with the Dockerfile `from` names (in the module's
directory or a subdirectory of it) as file and the module's own directory as context, so a COPY names paths from the
module's root (issue 1703), `--target`, and the images `build.on` pins as `--build-arg`. Answers the image's digest. With
`publish: true` (a merged trunk commit only) the module's own process then pushes it to the registry under
`<module>/<artifact>:<tag of its own>` (the first 12 hex digits of the digest unless the call gives one), waiting out
a registry held still, and the key carries the digest the registry serves and the reference pinned by it. Without
`publish` (a pull request) nothing is pushed.
- `build_image_copy` copies an upstream artifact (`module`, `artifact`, `from`) registry to registry into the artifact store
over the registry API, the whole index and every blob, by the digest the manifest pins (ADR 0498 §4), and verifies the
store serves that digest. Answers the digest and the reference `<registry>/<module>/<artifact>@sha256:<digest>`. A
`from` without a digest fails `not pinned:`. It needs no workspace and no buildah.
- It refuses, naming why, an image whose build context is a whole repository (`context`) or whose `build.on` names
another module's artifact (`unsupportedImage` in `build.go`): those forms belong to the images build-agent builds
until it is removed at R4g.
- `build_image_release {workspace}` removes a workspace at once (no guard, no floor; one already gone is no error, one a
running step holds is refused); its caller calls it once its last step answered. A failed fetch removes its own
workspace, and one no caller released is removed two hours after its last use (ADR 0509).
- A fetch and a build are refused, `refused: <node> has N GiB free under the floor of 20 GiB (on <dir>)`, and the health
is unhealthy with that text, while the work directory's or the image store's filesystem has less than 20 GiB free
(issue 1839). A copy writes nothing to the disk and is not refused.
- `build_image_step`, `build_image_steps`, `build_image_status` and `build_image_health` read.
The registry's address is placed into `<home>/registry` as `${seat:mesh-artifact-store:reach}` (host:port): the provisioner's client fills a moved module's placement, and the artifact store has not moved, so a `${bound:artifact-store:...}` is refused at composition (issue 1703).