#!/usr/bin/env bash
# Builds the frozen dangerlab template on this host. The template is booted exactly
# once, here, to run cloud-init; after that it is read-only forever and every test runs
# against a throwaway clone of it. Idempotent enough to re-run.
set -euo pipefail
source /home/user/dangerlab/dangerlab-lib.sh
set -euo pipefail

IMG_URL=https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2
DL=$LAB/images/debian-13-genericcloud-amd64.qcow2
PROV=dangerlab-template
WORK=$VAR/template-work.qcow2
PROV_MAC=52:54:00:da:1a:b0
PROV_IP=192.168.122.100

sudo -n install -d -o libvirt-qemu -g libvirt-qemu -m 0755 "$VAR"
sudo -n install -d -o "$(id -un)" -g "$(id -gn)" -m 0755 "$RUNDIR"
mkdir -p "$LAB/images"

[[ -f "$KEY" ]] || ssh-keygen -q -t ed25519 -N '' -f "$KEY" -C dangerlab
[[ -f "$DL" ]] || curl -fsSL -o "$DL" "$IMG_URL"

# The seed is shared by every clone, so the guest identity (hostname, cloud-init
# instance-id) is lineage, not per-instance state. cloud-init is NOT cleaned before the
# freeze: a clone then finds its instance already provisioned and skips the first-boot
# package work, which is what makes a clone boot in seconds.
CI=$LAB/ci
mkdir -p "$CI"
cat >"$CI/user-data" <<EOF
#cloud-config
hostname: dangerlab
users:
  - name: lab
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    lock_passwd: false
    ssh_authorized_keys:
      - $(cat "$KEY.pub")
package_update: true
packages: [git, procps, coreutils, qemu-guest-agent]
write_files:
  - path: $LINEAGE_FILE
    owner: 'root:root'
    permissions: '0444'
    content: |
      $LINEAGE_ID
  - path: /etc/default/grub.d/99-dangerlab-serial.cfg
    content: |
      GRUB_CMDLINE_LINUX_DEFAULT="console=tty0 console=ttyS0,115200n8"
      GRUB_TERMINAL="console serial"
      GRUB_SERIAL_COMMAND="serial --unit=0 --speed=115200"
runcmd:
  - [systemctl, enable, --now, qemu-guest-agent]
  - [systemctl, enable, serial-getty@ttyS0.service]
  - [update-grub]
  - [touch, /var/lib/cloud/dangerlab-ready]
EOF
printf 'instance-id: dangerlab-1\nlocal-hostname: dangerlab\n' > "$CI/meta-data"
cloud-localds "$LAB/seed.iso" "$CI/user-data" "$CI/meta-data"
sudo -n cp "$LAB/seed.iso" "$SEED"

net_reserve
$V net-update default delete ip-dhcp-host "<host mac='$PROV_MAC'/>" --live --config >/dev/null 2>&1 || true
$V net-update default add ip-dhcp-host "<host mac='$PROV_MAC' name='dangerlab-template' ip='$PROV_IP'/>" --live --config >/dev/null

$V destroy "$PROV" >/dev/null 2>&1 || true
$V undefine "$PROV" >/dev/null 2>&1 || true
sudo -n rm -f "$WORK"
sudo -n cp "$DL" "$WORK"
sudo -n qemu-img resize "$WORK" 10G >/dev/null
sudo -n touch "$VAR/template.console.log"
sudo -n chown libvirt-qemu:libvirt-qemu "$WORK" "$SEED" "$VAR/template.console.log"

cat >/tmp/dangerlab-template.xml <<XML
<domain type='kvm'>
  <name>$PROV</name>
  <memory unit='KiB'>2097152</memory>
  <currentMemory unit='KiB'>2097152</currentMemory>
  <vcpu placement='static'>2</vcpu>
  <resource><partition>/machine/dangerlab/slot1</partition></resource>
  <os>
    <type arch='x86_64' machine='q35'>hvm</type>
    <boot dev='hd'/>
  </os>
  <features><acpi/><apic/></features>
  <cpu mode='host-passthrough' check='none'/>
  <clock offset='utc'/>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>
  <devices>
    <emulator>/usr/bin/qemu-system-x86_64</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <source file='$WORK'/>
      <target dev='vda' bus='virtio'/>
    </disk>
    <disk type='file' device='cdrom'>
      <driver name='qemu' type='raw'/>
      <source file='$SEED'/>
      <target dev='sda' bus='sata'/>
      <readonly/>
    </disk>
    <interface type='network'>
      <source network='default'/>
      <mac address='$PROV_MAC'/>
      <model type='virtio'/>
    </interface>
    <serial type='file'>
      <source path='$VAR/template.console.log'/>
      <target port='0'/>
    </serial>
    <console type='file'>
      <source path='$VAR/template.console.log'/>
      <target type='serial' port='0'/>
    </console>
    <graphics type='vnc' port='-1' listen='127.0.0.1'/>
    <video><model type='vga'/></video>
    <memballoon model='none'/>
  </devices>
</domain>
XML
$V define /tmp/dangerlab-template.xml >/dev/null
$V start "$PROV" >/dev/null

note "waiting for cloud-init to finish (first boot installs packages)"
IP=$PROV_IP
deadline=$(( SECONDS + 600 ))
reachable=0
while (( SECONDS < deadline )); do
  if $SSH lab@"$IP" true 2>/dev/null; then reachable=1; break; fi
  sleep 3
done
(( reachable )) || die "template guest never became reachable at $IP; see $VAR/template.console.log"

$SSH lab@"$IP" 'sudo cloud-init status --wait >/dev/null 2>&1; test -f /var/lib/cloud/dangerlab-ready && command -v git >/dev/null' \
  || die "cloud-init did not complete cleanly"
$SSH lab@"$IP" "grep -qxF '$LINEAGE_ID' $LINEAGE_FILE" \
  || die "lineage marker missing from the template — danger-guard would refuse in every clone"

# cloud-init renders a .network file that matches the interface by the MAC the template
# was built with, and it does not re-render in a clone (same instance-id). Every clone
# would boot with an interface no unit matches and never request an address. Replace it
# with a MAC-agnostic match and stop cloud-init from writing network config again.
$SSH lab@"$IP" "sudo rm -f /etc/systemd/network/*cloud-init*.network /etc/network/interfaces.d/*cloud-init*
  printf 'network: {config: disabled}\n' | sudo tee /etc/cloud/cloud.cfg.d/99-dangerlab-no-net.cfg >/dev/null
  printf '[Match]\nName=en*\n\n[Network]\nDHCP=ipv4\n\n[DHCPv4]\nClientIdentifier=mac\n' \
    | sudo tee /etc/systemd/network/10-dangerlab-dhcp.network >/dev/null
  sudo systemctl enable systemd-networkd >/dev/null 2>&1" \
  || die "could not install the clone-portable network config in the template"

# Golden-image reset of the host identity. systemd-networkd derives its DHCP client
# identifier from /etc/machine-id, so clones that shared one were handed each other's
# lease and only the first of them got an address. An empty file makes systemd mint a
# fresh id into each clone's overlay at boot.
$SSH lab@"$IP" 'sudo truncate -s 0 /etc/machine-id
                if [ -f /var/lib/dbus/machine-id ] && [ ! -L /var/lib/dbus/machine-id ]; then
                  sudo ln -sf /etc/machine-id /var/lib/dbus/machine-id
                fi' || die "could not reset the template machine-id"

$SSH lab@"$IP" 'sudo poweroff' >/dev/null 2>&1 || true
for _ in $(seq 1 60); do [[ "$(vm_state "$PROV")" == "shut off" ]] && break; sleep 2; done
domain_kill "$PROV" || die "template guest would not shut down"

# Freeze. The previous template is kept until the new one verifies.
[[ -f "$BASE" ]] && sudo -n mv "$BASE" "$BASE.prev"
sudo -n mv "$WORK" "$BASE"
sudo -n chmod 444 "$BASE"
sudo -n chown libvirt-qemu:libvirt-qemu "$BASE"
sudo -n sh -c "sha256sum '$BASE' > '$BASE_SUM'"
base_verify
sudo -n rm -f "$BASE.prev"

for k in $(slot_seq); do
  sudo -n install -d -o libvirt-qemu -g libvirt-qemu -m 0755 "$(slot_dir "$k")"
  sudo -n touch "$(slot_console "$k")"
  sudo -n chown libvirt-qemu:libvirt-qemu "$(slot_console "$k")"
  sudo -n rm -f "$(slot_overlay "$k")"
  domain_kill "$(slot_dom "$k")" >/dev/null 2>&1 || true
done

note "template frozen: $BASE ($(stat -c %A "$BASE")), $SLOTS clone slots ready"
