106 lines
3.4 KiB
Text
106 lines
3.4 KiB
Text
|
|
{%- set role = image_role -%}
|
||
|
|
#!/bin/bash
|
||
|
|
# nixos-{{ role.name }} — build image and create Hetzner snapshot
|
||
|
|
# provisioning {{ provisioning_version }} — {{ now }}
|
||
|
|
# Output: SNAPSHOT_ID written to stdout on success
|
||
|
|
set -euo pipefail
|
||
|
|
{%- if debug is defined and debug == "yes" %}
|
||
|
|
{%- endif %}
|
||
|
|
|
||
|
|
HCLOUD_TOKEN="${HCLOUD_TOKEN:?HCLOUD_TOKEN required}"
|
||
|
|
PROVISIONING="${PROVISIONING:-/usr/local/provisioning}"
|
||
|
|
FLAKE_DIR="{{ flake_dir }}"
|
||
|
|
SSH_KEY="{{ ssh_key }}"
|
||
|
|
LOCATION="{{ location | default(value='nbg1') }}"
|
||
|
|
ROLE="{{ role.name }}"
|
||
|
|
|
||
|
|
# Build the raw-efi image via nix
|
||
|
|
# Requires: nix with aarch64-linux builder (Nix Desktop on Apple Silicon, or remote builder)
|
||
|
|
echo "=== Building NixOS $ROLE image ==="
|
||
|
|
|
||
|
|
# Find nix executable (handle both installed and path variations)
|
||
|
|
NIX_BIN=$(which nix 2>/dev/null || find /nix/store -name "nix" -type f 2>/dev/null | head -1)
|
||
|
|
if [ -z "$NIX_BIN" ]; then
|
||
|
|
echo "ERROR: nix not found in PATH or /nix/store"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
export PROVISIONING
|
||
|
|
$NIX_BIN build "${FLAKE_DIR}#packages.aarch64-linux.${ROLE}-image" \
|
||
|
|
--out-link "/tmp/nixos-${ROLE}-image" \
|
||
|
|
--print-build-logs \
|
||
|
|
--impure
|
||
|
|
|
||
|
|
IMAGE_STORE=$(readlink -f "/tmp/nixos-${ROLE}-image")
|
||
|
|
# nixos-generators raw-efi output is a .img file inside the store path
|
||
|
|
IMAGE_PATH=$(find "$IMAGE_STORE" -name "*.img" | head -1)
|
||
|
|
if [ -z "$IMAGE_PATH" ]; then
|
||
|
|
echo "ERROR: no .img found in $IMAGE_STORE"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
echo "Image: $IMAGE_PATH ($(du -sh "$IMAGE_PATH" | cut -f1))"
|
||
|
|
|
||
|
|
# Create temporary build server (cheapest ARM64 on Hetzner)
|
||
|
|
TEMP_NAME="imgbuild-${ROLE}-$$"
|
||
|
|
echo "=== Creating temp server $TEMP_NAME ==="
|
||
|
|
hcloud server create \
|
||
|
|
--name "$TEMP_NAME" \
|
||
|
|
--type cax11 \
|
||
|
|
--location "$LOCATION" \
|
||
|
|
--image debian-12 \
|
||
|
|
--ssh-key "$SSH_KEY" \
|
||
|
|
--output json > /tmp/imgbuild-create.json
|
||
|
|
|
||
|
|
SERVER_ID=$(hcloud server describe "$TEMP_NAME" -o format='{{ "{{.ID}}" }}')
|
||
|
|
SERVER_IP=$(hcloud server describe "$TEMP_NAME" -o format='{{ "{{.PublicNet.IPv4.IP}}" }}')
|
||
|
|
echo "Server $TEMP_NAME: id=$SERVER_ID ip=$SERVER_IP"
|
||
|
|
|
||
|
|
cleanup() {
|
||
|
|
echo "=== Cleanup: deleting $TEMP_NAME ==="
|
||
|
|
hcloud server delete "$SERVER_ID" || true
|
||
|
|
rm -f /tmp/imgbuild-create.json "/tmp/nixos-${ROLE}-image"
|
||
|
|
}
|
||
|
|
trap cleanup EXIT
|
||
|
|
|
||
|
|
# Enable rescue and reboot
|
||
|
|
echo "=== Booting into rescue ==="
|
||
|
|
hcloud server enable-rescue "$SERVER_ID" --type linux64 --ssh-key "$SSH_KEY"
|
||
|
|
hcloud server reboot "$SERVER_ID"
|
||
|
|
|
||
|
|
echo "Waiting for rescue SSH..."
|
||
|
|
for i in $(seq 1 24); do
|
||
|
|
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 \
|
||
|
|
-o BatchMode=yes root@"$SERVER_IP" true 2>/dev/null && break
|
||
|
|
sleep 10
|
||
|
|
done
|
||
|
|
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 \
|
||
|
|
-o BatchMode=yes root@"$SERVER_IP" true || {
|
||
|
|
echo "ERROR: rescue SSH unreachable after 240s"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
# Write image to disk
|
||
|
|
echo "=== Writing image to /dev/sda ==="
|
||
|
|
gzip -dc "$IMAGE_PATH" | ssh -o StrictHostKeyChecking=no root@"$SERVER_IP" \
|
||
|
|
"dd of=/dev/sda bs=4M conv=fsync status=progress"
|
||
|
|
|
||
|
|
echo "=== Powering off ==="
|
||
|
|
hcloud server poweroff "$SERVER_ID"
|
||
|
|
sleep 15
|
||
|
|
|
||
|
|
# Create snapshot
|
||
|
|
DESCRIPTION="nixos-${ROLE}-aarch64-{{ now | replace(from=' ', to='T') | replace(from=':', to='') }}"
|
||
|
|
echo "=== Creating snapshot: $DESCRIPTION ==="
|
||
|
|
SNAPSHOT_ID=$(hcloud server create-image "$SERVER_ID" \
|
||
|
|
--type snapshot \
|
||
|
|
--description "$DESCRIPTION" \
|
||
|
|
-o format='{{ "{{.ID}}" }}')
|
||
|
|
|
||
|
|
echo "=== Done ==="
|
||
|
|
echo "SNAPSHOT_ID=$SNAPSHOT_ID"
|
||
|
|
echo "Update servers.ncl for role '$ROLE': image = \"$SNAPSHOT_ID\""
|
||
|
|
|
||
|
|
# Disable cleanup trap (keep snapshot, delete server still happens)
|
||
|
|
trap - EXIT
|
||
|
|
hcloud server delete "$SERVER_ID"
|