#!/bin/sh
# bty-grow-rootfs - extend the server appliance's rootfs partition
# to fill the disk on first boot.
#
# The cooked server image is baked at a fixed 6 GiB disk size to
# keep the .img.gz small and dd-time fast. After the operator
# dd's it to a real server disk (typically 100s of GB), the
# rootfs is still only 6 GiB and bty-web's image catalog +
# state DB run out of headroom fast. This script grows the
# rootfs partition + filesystem to fill the actual disk so the
# server scales to whatever hardware the operator brought.
#
# Driven by ``bty-grow-rootfs.service``; sentinel-gated so it
# runs exactly once over the appliance's lifetime.
#
# Best-effort: any failure exits 0 (the appliance still boots
# with whatever space it has).

set -eu

log() {
    printf 'bty-grow-rootfs: %s\n' "$*"
}

# Identify the device backing /. Typical formats:
#   /dev/sda1, /dev/vda1, /dev/nvme0n1p1, /dev/mmcblk0p1
root_dev=$(findmnt -no SOURCE / 2>/dev/null || true)
if [ -z "$root_dev" ] || [ ! -b "$root_dev" ]; then
    log "could not resolve root device (got: ${root_dev:-empty}); skipping"
    exit 0
fi

# Parent disk + partition number. lsblk PKNAME returns ``sda``,
# ``vda``, ``nvme0n1``, etc.
disk_name=$(lsblk -no PKNAME "$root_dev" 2>/dev/null || true)
if [ -z "$disk_name" ]; then
    log "could not resolve parent disk of $root_dev; skipping"
    exit 0
fi
disk="/dev/${disk_name}"

# Trailing digits of $root_dev are the partition number. ``sed``
# regex matches ``[non-digit][digits]$``; works for sda1, vda1,
# nvme0n1p1, mmcblk0p1.
part_num=$(printf '%s' "$root_dev" | sed -E 's/^.*[^0-9]([0-9]+)$/\1/')
if ! [ "$part_num" -gt 0 ] 2>/dev/null; then
    log "could not extract partition number from $root_dev; skipping"
    exit 0
fi

log "growing $root_dev (partition $part_num on $disk) to fill disk"

# growpart from cloud-utils does the partition-table edit (handles
# both MBR and GPT, deals with the GPT secondary header location,
# preserves bootable flag, etc.). It's idempotent: a no-op if the
# partition already covers the whole tail of the disk, which is
# the case when the cooked image was dd'd onto a stick exactly
# its own size.
if ! growpart "$disk" "$part_num"; then
    log "growpart returned non-zero (already at full size?); continuing"
fi

# Re-read the partition table so the kernel sees the new size.
partprobe "$disk" 2>/dev/null || true
udevadm settle 2>/dev/null || true

# Resize the filesystem inside the now-larger partition. The bty
# server image is ext4 by default (Debian cloud-image rootfs).
# resize2fs is idempotent.
if ! resize2fs "$root_dev"; then
    log "resize2fs returned non-zero; continuing"
fi

log "rootfs grow complete"
