From 67f878876818d446981d735b05c284fc0edda9bc Mon Sep 17 00:00:00 2001 From: navigator Date: Thu, 8 Oct 2026 06:57:58 +0000 Subject: [PATCH] feat: bt-layer --boot builds an appliance's boot layer Decision 0052: the kernel, initrd, firmware, bootloaders and installer leave the shared layers (common's plans/turnkey/base) for a boot layer per appliance, which only the ISO uses. bt-layer --boot APPLIANCE builds APPLIANCE-boot on top of the appliance's layer from products/boot ($BT_BOOT_PRODUCT to use another), whose plan is common's plans/keel-boot and whose conf and overlay are keel-boot. The container template is assembled from the appliance's chain without it. --- .gitignore | 1 + bin/layer-lib | 22 +++++++++++++++++++ bt-layer | 12 +++++++++++ products/boot/Makefile | 14 ++++++++++++ products/boot/plan/main | 2 ++ tests/layer | 47 ++++++++++++++++++++++++++++++++++++++--- 6 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 products/boot/Makefile create mode 100644 products/boot/plan/main diff --git a/.gitignore b/.gitignore index ebca8d3..049959d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ config logs/* tmp/* +products/boot/build/ diff --git a/bin/layer-lib b/bin/layer-lib index b8518e8..d52951a 100755 --- a/bin/layer-lib +++ b/bin/layer-lib @@ -36,6 +36,11 @@ LAYER_SUBMOUNTS="run dev/pts dev proc sys" LAYER_ZSTD_LEVEL=19 +# The boot layer of an appliance (handbook decision 0052) is named after it +# with this suffix: the kernel, initrd, bootloaders and installer the ISO +# boots with and the container template never carries. +LAYER_BOOT_SUFFIX=-boot + # A unit is a component supplied to fab as a directory under the product's # UNIT_DIRS, carrying any of these four inputs. fab resolves the plan, applies # the overlay, runs the conf script and applies the removelist. @@ -852,10 +857,15 @@ layer_parent_resolve() { layer_manifest_get "$manifest" sha256 } +# layer_parse_args ARGS...: sets LAYER_NAME, LAYER_PARENT, LAYER_USE_EXISTING, +# LAYER_BOOT and LAYER_HELP, or LAYER_ERROR and returns 1. With --boot the +# name given is the appliance: the layer is APPLIANCE-boot and its parent is +# the appliance's layer (handbook decision 0052). layer_parse_args() { LAYER_NAME= LAYER_PARENT= LAYER_USE_EXISTING= + LAYER_BOOT= LAYER_HELP= LAYER_ERROR= while [[ $# -gt 0 ]]; do @@ -875,6 +885,9 @@ layer_parse_args() { --use-existing|-u) LAYER_USE_EXISTING=yes ;; + --boot|-b) + LAYER_BOOT=yes + ;; -*) LAYER_ERROR="unrecognized option: $1" return 1 @@ -897,5 +910,14 @@ layer_parse_args() { LAYER_ERROR="a layer cannot be its own parent" return 1 fi + if [[ -n "$LAYER_BOOT" ]]; then + if [[ -n "$LAYER_PARENT" ]]; then + # shellcheck disable=SC2034 # read by bt-layer: usage "$LAYER_ERROR" + LAYER_ERROR="--boot builds on the appliance it names; it takes no --parent" + return 1 + fi + LAYER_PARENT=$LAYER_NAME + LAYER_NAME=$LAYER_NAME$LAYER_BOOT_SUFFIX + fi return 0 } diff --git a/bt-layer b/bt-layer index 3b1bf26..68cb08d 100755 --- a/bt-layer +++ b/bt-layer @@ -19,6 +19,7 @@ info() { echo "INFO [$APP]: $*"; } usage() { cat< diff --git a/tests/layer b/tests/layer index 8c06a2c..7f9a13d 100755 --- a/tests/layer +++ b/tests/layer @@ -46,7 +46,8 @@ # layer_hash_size (ok, missing); layer_write_sha256; layer_umount_targets # (none, mounted); layer_parent_resolve (ok, no rootfs, no manifest); # layer_parse_args (help, name, parent, use-existing, no name, two -# names, unknown option, parent without value, self parent). +# names, unknown option, parent without value, self parent, --boot, +# --boot with --parent). # bt-layer: --help; usage error; missing config; missing FAB_PATH; missing # product; bad epoch; rootfs build with warning on BT_GPGKEY, exported # with an empty machine-id whatever the build left, refused when it @@ -57,7 +58,9 @@ # stale altstrap symlink; parent manifest without conf list; product # without a debug target; failing make removes the altstrap; TERM default; # a rootfs layer with a unit; a child that composes the same unit; a child -# that adds one; a child with no unit of its own; a unit pinned to another +# that adds one; a child with no unit of its own; --boot (the boot layer +# on the appliance's, its product missing, with --parent; products/boot's +# conf and overlays); a unit pinned to another # version than the parent applied; a unit whose conf fab would skip; a # parent manifest written before the units field existed. @@ -753,7 +756,11 @@ layer_parse_args -u lamp --parent core assert_eq parse-parent "$LAYER_NAME|$LAYER_PARENT|$LAYER_USE_EXISTING" "lamp|core|yes" layer_parse_args wordpress -p lamp --use-existing assert_eq parse-short "$LAYER_NAME|$LAYER_PARENT|$LAYER_USE_EXISTING" "wordpress|lamp|yes" -for bad in "" "a b" "--bogus a" "a --parent" "a --parent a"; do +layer_parse_args --boot core +assert_eq parse-boot "$LAYER_NAME|$LAYER_PARENT|$LAYER_BOOT" "core-boot|core|yes" +layer_parse_args -u -b web +assert_eq parse-boot-short "$LAYER_NAME|$LAYER_PARENT|$LAYER_USE_EXISTING" "web-boot|web|yes" +for bad in "" "a b" "--bogus a" "a --parent" "a --parent a" "a --boot --parent b"; do if layer_parse_args $bad 2>/dev/null; then fatal "parse_args should have failed on '$bad'" fi @@ -969,5 +976,39 @@ assert_eq app5-build-units "$(layer_manifest_get "$L/app5.manifest" build_units) "broken@1.0" assert_eq app5-units-used "$(cat "$tmp/products/app5/build/units.used")" "unit.d/broken" +info "23: bt-layer --boot core (the boot layer of decision 0052)" +# the product bt-layer --boot builds, here a stub; the real one is checked +# below against a stand-in for common's turnkey.mk +write_product "$tmp/products/bootprod" "turnkey.d keel-boot" "turnkey.d keel-boot" +expect_fail boot-default-product-missing "$sbt/bt-layer" --boot core +out=$(BT_BOOT_PRODUCT=$tmp/products/bootprod "$sbt/bt-layer" --boot core) +assert_file "$L/core-boot.tar.zst" +assert_eq boot-type "$(layer_manifest_get "$L/core-boot.manifest" type)" delta +assert_eq boot-parent "$(layer_manifest_get "$L/core-boot.manifest" parent)" core +assert_eq boot-parent-sha "$(layer_manifest_get "$L/core-boot.manifest" parent_sha256)" \ + "$(layer_manifest_get "$L/core.manifest" sha256)" +assert_eq boot-build-conf "$(layer_manifest_get "$L/core-boot.manifest" build_conf)" \ + "turnkey.d/hostname keel-boot" +assert_eq boot-build-overlays "$(layer_manifest_get "$L/core-boot.manifest" build_overlays)" \ + "turnkey.d/tkl-bashlib turnkey.d/systemd-chroot keel-boot" +assert_eq boot-altstrap-used "$(cat "$tmp/products/bootprod/build/altstrap.used")" "$L/core.rootfs" +expect_fail boot-with-parent "$sbt/bt-layer" --boot core --parent lamp + +info "23a: products/boot asks for keel-boot on top of turnkey.d" +mkdir -p "$tmp/fabstub/common/mk" +cat > "$tmp/fabstub/common/mk/turnkey.mk" <<'MK' +COMMON_OVERLAYS := turnkey.d $(COMMON_OVERLAYS) +COMMON_CONF := turnkey.d $(COMMON_CONF) +debug: + $(foreach v,$(V),$(warning $(v) = $($(v)))) + @true +MK +cp -r "$BT/products/boot" "$tmp/bootcopy" +assert_eq boot-product-conf "$(FAB_PATH=$tmp/fabstub layer_make_var "$tmp/bootcopy" COMMON_CONF)" \ + "turnkey.d keel-boot" +assert_eq boot-product-overlays "$(FAB_PATH=$tmp/fabstub layer_make_var "$tmp/bootcopy" COMMON_OVERLAYS)" \ + "turnkey.d keel-boot" +grep -qx '#include ' "$tmp/bootcopy/plan/main" || fatal "products/boot does not resolve plans/keel-boot" + echo info "all tests passed"