Enhance eMMC provisioning dashboard: implement cloud-init template management with options to load, save, and delete templates. Update UI to support selecting Raspberry Pi OS variant for image building and improve user instructions for cloud-init image creation. Add new API endpoints for managing cloud-init templates and fetching the latest Raspberry Pi OS URLs.
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run on the Proxmox host when build_cloudinit_request.json appears in the provisioning dir.
|
||||
# Downloads Raspberry Pi OS image from URL, injects cloud-init NoCloud files, saves to BACKUPS_DIR.
|
||||
# Uses loop devices and mount (available on host, not in unprivileged LXC).
|
||||
# Triggered by systemd path unit cm4-build-cloudinit.path.
|
||||
|
||||
set -e
|
||||
PROV_DIR="${CM4_PROVISIONING_DIR:-/var/lib/cm4-provisioning}"
|
||||
REQUEST_FILE="$PROV_DIR/build_cloudinit_request.json"
|
||||
STATUS_FILE="$PROV_DIR/build_cloudinit_status.json"
|
||||
[[ -f /opt/cm4-provisioning/env ]] && source /opt/cm4-provisioning/env
|
||||
BACKUPS_DIR="${BACKUPS_DIR:-$PROV_DIR/backups}"
|
||||
GOLDEN_IMAGE="${GOLDEN_IMAGE:-$PROV_DIR/golden.img}"
|
||||
|
||||
write_status() {
|
||||
local phase="$1" message="$2" output_name="$3" error="$4"
|
||||
printf '{"phase":"%s","message":"%s","output_name":%s,"error":%s,"updated":%s}\n' \
|
||||
"$phase" "$message" \
|
||||
"$([ -n "$output_name" ] && echo "\"$output_name\"" || echo "null")" \
|
||||
"$([ -n "$error" ] && echo "\"${error//\"/\\\"}\"" || echo "null")" \
|
||||
"$(date +%s)" > "$STATUS_FILE"
|
||||
}
|
||||
|
||||
[[ -f "$REQUEST_FILE" ]] || { echo "No request file"; exit 0; }
|
||||
|
||||
TEMP_DIR=$(mktemp -d -p "$BACKUPS_DIR" cloudinit-build.XXXXXX)
|
||||
trap 'rm -rf "$TEMP_DIR"; rm -f "$REQUEST_FILE"' EXIT
|
||||
|
||||
# Extract fields from JSON into temp files (handles multi-line content)
|
||||
python3 - "$REQUEST_FILE" "$TEMP_DIR" << 'PY'
|
||||
import json, sys
|
||||
with open(sys.argv[1]) as f:
|
||||
d = json.load(f)
|
||||
out = sys.argv[2]
|
||||
for name, key in [("user-data", "user_data"), ("meta-data", "meta_data"), ("network-config", "network_config")]:
|
||||
with open(f"{out}/{name}", "w") as f:
|
||||
f.write(d.get(key, ""))
|
||||
with open(f"{out}/variant", "w") as f:
|
||||
f.write(d.get("variant", "lite"))
|
||||
with open(f"{out}/url", "w") as f:
|
||||
f.write(d.get("url", ""))
|
||||
with open(f"{out}/set_golden", "w") as f:
|
||||
f.write("1" if d.get("set_as_golden_after") else "0")
|
||||
PY
|
||||
|
||||
URL=$(cat "$TEMP_DIR/url")
|
||||
VARIANT=$(cat "$TEMP_DIR/variant")
|
||||
SET_AS_GOLDEN=$(cat "$TEMP_DIR/set_golden")
|
||||
[[ -n "$URL" ]] || { write_status "error" "" "" "Missing url in request"; exit 1; }
|
||||
|
||||
OUT_NAME="raspios-${VARIANT}-cloudinit-$(date +%Y%m%d-%H%M%S).img"
|
||||
OUT_PATH="$BACKUPS_DIR/$OUT_NAME"
|
||||
|
||||
write_status "downloading" "Downloading $(basename "$URL")…" "" ""
|
||||
XZ_FILE="$TEMP_DIR/image.img.xz"
|
||||
if ! curl -f -sS -L -o "$XZ_FILE" "$URL"; then
|
||||
write_status "error" "" "" "Download failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
write_status "decompressing" "Decompressing image…" "" ""
|
||||
IMG_FILE="$TEMP_DIR/image.img"
|
||||
xz -d -k -f "$XZ_FILE" || { write_status "error" "" "" "Decompress failed"; exit 1; }
|
||||
[[ -f "$IMG_FILE" ]] || { write_status "error" "" "" "image.img not found after decompress"; exit 1; }
|
||||
|
||||
write_status "injecting" "Mounting boot partition and injecting cloud-init…" "" ""
|
||||
LOOP=$(losetup -f --show -P "$IMG_FILE")
|
||||
boot_part="${LOOP}p1"
|
||||
[[ -b "$boot_part" ]] || boot_part="${LOOP}p2"
|
||||
[[ -b "$boot_part" ]] || { write_status "error" "" "" "Boot partition not found"; losetup -d "$LOOP"; exit 1; }
|
||||
|
||||
MNT="$TEMP_DIR/mnt"
|
||||
mkdir -p "$MNT"
|
||||
mount "$boot_part" "$MNT"
|
||||
cp "$TEMP_DIR/user-data" "$MNT/user-data"
|
||||
cp "$TEMP_DIR/meta-data" "$MNT/meta-data"
|
||||
cp "$TEMP_DIR/network-config" "$MNT/network-config"
|
||||
umount "$MNT"
|
||||
losetup -d "$LOOP"
|
||||
|
||||
write_status "finalizing" "Copying image to backups…" "" ""
|
||||
mkdir -p "$BACKUPS_DIR"
|
||||
cp "$IMG_FILE" "$OUT_PATH"
|
||||
|
||||
if [[ "$SET_AS_GOLDEN" == "1" ]]; then
|
||||
cp "$OUT_PATH" "$GOLDEN_IMAGE"
|
||||
write_status "done" "Built $OUT_NAME and set as golden image." "$OUT_NAME" ""
|
||||
else
|
||||
write_status "done" "Built $OUT_NAME" "$OUT_NAME" ""
|
||||
fi
|
||||
Reference in New Issue
Block a user