#!/bin/bash
# Lima prepare script for kernel testing with bootc images.
#
# Referenced from .cui-cui.toml as:
#   prepare = "@runners/lima-prepare.sh"
#
# Available variables (exported by cui-cui before this script runs):
#   $CI_JOB_NAME          - job name (e.g. "kernel-test")
#   $CI_JOB_IMAGE         - bootc container image (e.g. "registry.example.com/fedora-bootc:41")
#   $LIMA_KEXEC_ARTIFACT  - artifact path to kexec into (e.g. "vmlinux@build-kernel")
#
# Required tools on the host: limactl, podman, bootc-image-builder (as container)
#
# Environment variables (set in .cui-cui.toml or shell):
#   $LIMA_IMAGES_PATH - where to cache qcow2 images (default: ~/.cache/cui-cui/lima-images)

set -eo pipefail

# --- configuration ---

LIMA_IMAGES_PATH="${LIMA_IMAGES_PATH:-$HOME/.cache/cui-cui/lima-images}"

slugify() {
    echo "$1" | sed -E 's/[^a-zA-Z0-9]+/-/g' | tr A-Z a-z
}

export INSTANCE=$(slugify "${CI_JOB_NAME}")
IMAGE_SLUG=$(slugify "$CI_JOB_IMAGE")
VM_IMAGE="$LIMA_IMAGES_PATH/${IMAGE_SLUG}.qcow2"
VM_WORK_DIR="$LIMA_IMAGES_PATH/$INSTANCE"

mkdir -p "$VM_WORK_DIR"

# --- handle kexec artifact ---
# If LIMA_KEXEC_ARTIFACT is set, the vmlinux file should already be in
# the working directory (uploaded by cui-cui's artifact transfer).
# Copy it to the VM work dir for later use.

if [ -n "$LIMA_KEXEC_ARTIFACT" ]; then
    # LIMA_KEXEC_ARTIFACT is "path@job", extract just the path part
    KEXEC_PATH="${LIMA_KEXEC_ARTIFACT%%@*}"
    if [ -f "$KEXEC_PATH" ]; then
        echo "Found kexec kernel at $KEXEC_PATH"
        cp "$KEXEC_PATH" "$VM_WORK_DIR/vmlinux"
    else
        echo "Warning: LIMA_KEXEC_ARTIFACT set but $KEXEC_PATH not found"
    fi
fi

# --- build qcow2 from bootc image (cached) ---

if [ ! -f "$VM_IMAGE" ]; then
    echo "Pulling bootc image ${CI_JOB_IMAGE}..."
    podman inspect "$CI_JOB_IMAGE" > /dev/null 2>&1 || \
        podman pull "$CI_JOB_IMAGE"

    echo "Customizing image (adding git, build tools)..."
    podman build -t "${CI_JOB_IMAGE}-ci" -f - "$VM_WORK_DIR" <<DOCKERFILE
FROM $CI_JOB_IMAGE
RUN dnf install -y git-core git-lfs cloud-init wget curl && dnf clean all
DOCKERFILE

    echo "Converting to qcow2 with bootc-image-builder..."
    podman run \
        --rm \
        --privileged \
        --pull=newer \
        --security-opt label=type:unconfined_t \
        -v "$LIMA_IMAGES_PATH:/output" \
        -v /var/lib/containers/storage:/var/lib/containers/storage \
        quay.io/centos-bootc/bootc-image-builder:latest \
        --type qcow2 \
        --local \
        --rootfs xfs "${CI_JOB_IMAGE}-ci"

    mv "$LIMA_IMAGES_PATH/qcow2/disk.qcow2" "$VM_IMAGE"
    rm -rf "$LIMA_IMAGES_PATH/qcow2"

    echo "Cached qcow2 image at $VM_IMAGE"
else
    echo "Reusing cached qcow2 image at $VM_IMAGE"
fi

# --- create and start Lima VM ---

cat <<YAML | limactl create --name="$INSTANCE" --plain --tty=false -
images:
- location: "file://$VM_IMAGE"
  arch: "aarch64"

containerd:
  system: false
  user: false

provision:
  - mode: boot
    script: |
      # SELinux policy for Lima vsock/clock access
      cat <<__EOF__ > /tmp/lima-local.te
      module local 1.0;

      require {
        type clock_device_t;
        type vsock_device_t;
        type init_t;
        class chr_file { ioctl open read };
        class vsock_socket { accept listen };
      }

      allow init_t clock_device_t:chr_file { ioctl open read };
      allow init_t self:vsock_socket { accept listen };
      allow init_t vsock_device_t:chr_file { ioctl open read };
      __EOF__

      checkmodule -M -m -o /tmp/lima-local.mod /tmp/lima-local.te
      semodule_package -o /tmp/lima-local.pp -m /tmp/lima-local.mod
      semodule -i /tmp/lima-local.pp

vmOpts:
  vz:
    rosetta:
      enabled: true
      binfmt: true
YAML

limactl start "$INSTANCE"

# --- kexec into custom kernel (if provided) ---

if [ -f "$VM_WORK_DIR/vmlinux" ]; then
    echo "Kexec to provided kernel..."
    limactl copy "$VM_WORK_DIR/vmlinux" "$INSTANCE:/tmp/vmlinux"

    OSTREE=$(limactl shell "$INSTANCE" ls /boot/ostree/)
    INITRAMFS=$(limactl shell "$INSTANCE" ls "/boot/ostree/${OSTREE}" | grep initramfs)

    limactl shell "$INSTANCE" sudo kexec --debug \
        --load /tmp/vmlinux \
        --reuse-cmdline \
        --initrd="/boot/ostree/${OSTREE}/${INITRAMFS}"
    limactl shell "$INSTANCE" sudo systemctl kexec

    # Wait for VM to come back after kexec
    RETRIES=0
    until (( RETRIES == 10 )) || limactl shell "$INSTANCE" uname -r; do
        echo -n .
        sleep 1
        (( RETRIES++ ))
    done
fi

# --- copy project files into the VM ---
# The wrapper extracted artifacts into $PWD (the host worktree) before
# running this prepare script. Copy everything into the VM so the CI
# script can find artifacts (selftests, binaries, etc.) at their
# expected relative paths.
echo "Copying project files into the VM..."
# Use limactl copy to rsync files directly into the VM user's home.
# The trailing /. copies the directory *contents* (not the directory itself).
# limactl shell defaults to ~, so the job script finds files at relative paths.
limactl copy "$(pwd)/." "$INSTANCE:~/"

limactl shell "$INSTANCE" uname -r
echo "Lima instance '$INSTANCE' is ready."
