init
| 1 | #!/bin/sh |
| 2 | # PID 1 of the build VM. It builds the context from the job disk with |
| 3 | # buildah and writes the image to the "hf.image" port as a tar of a |
| 4 | # containers-image dir: layout. |
| 5 | # Every exit path reboots, and QEMU runs with -no-reboot, so it exits. |
| 6 | |
| 7 | finish() { |
| 8 | echo "$1" |
| 9 | sync |
| 10 | reboot -f |
| 11 | } |
| 12 | |
| 13 | # crun cannot pivot_root out of the initramfs, so the rest runs on a tmpfs. |
| 14 | if [ ! -e /.hf-tmpfs ]; then |
| 15 | mount -t tmpfs -o mode=0755 tmpfs /mnt |
| 16 | tar -c -C / --exclude=./mnt . | tar -x -C /mnt |
| 17 | touch /mnt/.hf-tmpfs |
| 18 | exec switch_root /mnt /init |
| 19 | fi |
| 20 | |
| 21 | mount -t devtmpfs dev /dev |
| 22 | exec </dev/null >/dev/console 2>&1 |
| 23 | mount -t proc proc /proc |
| 24 | mount -t sysfs sys /sys |
| 25 | mount -t cgroup2 cgroup2 /sys/fs/cgroup |
| 26 | mkdir -p /dev/pts /dev/shm |
| 27 | mount -t devpts devpts /dev/pts |
| 28 | mount -t tmpfs tmpfs /dev/shm |
| 29 | mount -t tmpfs tmpfs /tmp |
| 30 | mount -t tmpfs tmpfs /run |
| 31 | |
| 32 | for ko in /lib/hf-modules/*.ko; do |
| 33 | insmod "$ko" || finish "hearthforge: cannot load $ko" |
| 34 | done |
| 35 | |
| 36 | # QEMU user networking: fixed guest address, gateway and DNS. |
| 37 | ip link set lo up |
| 38 | ip link set eth0 up |
| 39 | ip addr add 10.0.2.15/24 dev eth0 |
| 40 | ip route add default via 10.0.2.2 |
| 41 | echo "nameserver 10.0.2.3" > /etc/resolv.conf |
| 42 | echo "127.0.0.1 localhost" > /etc/hosts |
| 43 | |
| 44 | job= |
| 45 | scratch= |
| 46 | for b in /sys/block/vd*; do |
| 47 | case $(cat "$b/serial" 2>/dev/null) in |
| 48 | hfjob) job=/dev/${b##*/} ;; |
| 49 | hfscratch) scratch=/dev/${b##*/} ;; |
| 50 | esac |
| 51 | done |
| 52 | [ -n "$job" ] && [ -n "$scratch" ] || finish "hearthforge: disks missing" |
| 53 | mkfs.ext4 -q -F -E lazy_itable_init=1,lazy_journal_init=1 "$scratch" || |
| 54 | finish "hearthforge: cannot format the scratch disk" |
| 55 | mount -o noatime "$scratch" /var/lib/containers || |
| 56 | finish "hearthforge: cannot mount the scratch disk" |
| 57 | # The root is a RAM tmpfs. The job and buildah's layer staging in /var/tmp |
| 58 | # go to the scratch disk instead. |
| 59 | hf=/var/lib/containers/hf |
| 60 | mkdir -p "$hf" /var/lib/containers/tmp |
| 61 | mount --bind /var/lib/containers/tmp /var/tmp |
| 62 | tar -x -f "$job" -C "$hf" || finish "hearthforge: cannot read the job" |
| 63 | |
| 64 | # The engine extracts the context under its own directory name. |
| 65 | ctx=$(find "$hf/context" -mindepth 1 -maxdepth 1) |
| 66 | [ -d "$ctx" ] || finish "hearthforge: the build context is not a single directory" |
| 67 | |
| 68 | out= |
| 69 | for port in /sys/class/virtio-ports/*; do |
| 70 | [ "$(cat "$port/name" 2>/dev/null)" = hf.image ] && out=/dev/${port##*/} |
| 71 | done |
| 72 | [ -n "$out" ] || finish "hearthforge: image port missing" |
| 73 | |
| 74 | set -- |
| 75 | [ -f "$hf/job/file" ] && set -- "$@" -f "$ctx/$(cat "$hf/job/file")" |
| 76 | [ -f "$hf/job/target" ] && set -- "$@" --target "$(cat "$hf/job/target")" |
| 77 | for f in "$hf"/job/args/*; do |
| 78 | [ -f "$f" ] && set -- "$@" --build-arg "${f##*/}=$(cat "$f")" |
| 79 | done |
| 80 | for f in "$hf"/job/secrets/*; do |
| 81 | [ -f "$f" ] && set -- "$@" --secret "id=${f##*/},src=$f" |
| 82 | done |
| 83 | |
| 84 | # Docker format keeps HEALTHCHECK, SHELL and ONBUILD, which OCI drops. |
| 85 | # --layers gives one layer per instruction, like docker build. |
| 86 | buildah build --format docker --layers --network host "$@" \ |
| 87 | -t localhost/hf-build "$ctx" || |
| 88 | finish "hearthforge: build failed" |
| 89 | buildah push --quiet --format v2s2 --compression-format gzip \ |
| 90 | localhost/hf-build dir:/var/lib/containers/hf-out || |
| 91 | finish "hearthforge: cannot export the image" |
| 92 | tar -c -f "$out" -C /var/lib/containers/hf-out . || |
| 93 | finish "hearthforge: cannot write the image" |
| 94 | finish "hearthforge: image written" |
| 95 |