diff --git a/README.md b/README.md index 638991f..b5802b7 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This repository builds two base images for Exordos Core project. The images cont The key features are: - [Universal Agent](https://github.com/infraguys/gcl_sdk/wiki/universal_agent) service. -- Exordos root autoresize service. Tries to perform resize of the root partition at every boot if it's possible. +- Exordos autoresize service. Grows partitions and filesystems (ext2/3/4 and xfs) up to the disk size, at every boot and, via a udev rule, when the hypervisor enlarges a disk of a running machine. - Exordos bootstrap service. Runs the bootstrap scripts. ## 🛠️ Build diff --git a/exordos/images/exordos_base/etc/systemd/exordos-root-autoresize.service b/exordos/images/exordos_base/etc/systemd/exordos-autoresize.service similarity index 57% rename from exordos/images/exordos_base/etc/systemd/exordos-root-autoresize.service rename to exordos/images/exordos_base/etc/systemd/exordos-autoresize.service index a92c2d0..a8dd2d1 100644 --- a/exordos/images/exordos_base/etc/systemd/exordos-root-autoresize.service +++ b/exordos/images/exordos_base/etc/systemd/exordos-autoresize.service @@ -1,8 +1,8 @@ [Unit] -Description=Exordos root autoresize one shot unit -DefaultDependencies=no +Description=Exordos disk autoresize one shot unit Conflicts=shutdown.target -Before=local-fs.target shutdown.target +After=local-fs.target +Before=shutdown.target [Service] Type=oneshot @@ -12,7 +12,7 @@ RestartSec=5s TimeoutStopSec=5 RemainAfterExit=yes StandardOutput=journal -ExecStart=/usr/bin/root_autoresize.sh +ExecStart=/usr/bin/exordos_autoresize.sh [Install] WantedBy=multi-user.target diff --git a/exordos/images/exordos_base/etc/systemd/exordos-autoresize@.service b/exordos/images/exordos_base/etc/systemd/exordos-autoresize@.service new file mode 100644 index 0000000..c5ef75e --- /dev/null +++ b/exordos/images/exordos_base/etc/systemd/exordos-autoresize@.service @@ -0,0 +1,9 @@ +[Unit] +Description=Exordos disk autoresize for /dev/%i +Conflicts=shutdown.target +Before=shutdown.target + +[Service] +Type=oneshot +StandardOutput=journal +ExecStart=/usr/bin/exordos_autoresize.sh /dev/%i diff --git a/exordos/images/exordos_base/etc/udev/90-exordos-autoresize.rules b/exordos/images/exordos_base/etc/udev/90-exordos-autoresize.rules new file mode 100644 index 0000000..725d8fd --- /dev/null +++ b/exordos/images/exordos_base/etc/udev/90-exordos-autoresize.rules @@ -0,0 +1,5 @@ +# The kernel emits a "change" uevent with RESIZE=1 when the hypervisor +# enlarges a disk of a running machine. Grow the partitions and filesystems +# on that disk so the new space becomes usable without a reboot. +ACTION=="change", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", ENV{RESIZE}=="1", \ + ENV{SYSTEMD_WANTS}+="exordos-autoresize@%k.service" diff --git a/exordos/images/exordos_base/exordos_autoresize.sh b/exordos/images/exordos_base/exordos_autoresize.sh new file mode 100755 index 0000000..5fb813c --- /dev/null +++ b/exordos/images/exordos_base/exordos_autoresize.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash + +# Copyright 2025-2026 Genesis Corporation +# +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# Grow partitions and filesystems up to the size of the underlying disk. +# +# Usage: +# exordos_autoresize.sh grow every mounted partition +# exordos_autoresize.sh grow the mounted partitions of one disk +# +# The first form runs once at boot and covers disks that were enlarged while +# the machine was off. The second form is triggered by udev when the +# hypervisor enlarges a disk of a running machine. + +set -eu +set -x +set -o pipefail + +log() { + echo "[exordos-autoresize] $*" +} + +wait_for_rw_root() { + while [[ "$(findmnt / -o options -n | grep -E "^ro,|,ro,|,ro$")" != "" ]] + do + log "Waiting for root partition to be in RW mode" + sleep 2 + done +} + +# List " " pairs of mounted partitions. +# Args: +# $1 - (optional) disk to limit the listing to, e.g. /dev/vda +list_mounted_partitions() { + local disk="${1:-}" + + # shellcheck disable=SC2086 + lsblk -nr -o PATH,TYPE,MOUNTPOINT ${disk:+"$disk"} \ + | awk '$2 == "part" && $3 != "" && $3 != "[SWAP]" {print $1, $3}' +} + +# Grow a single mounted partition and the filesystem on it. +# Args: +# $1 - partition device, e.g. /dev/vda1 +# $2 - mount point of that partition, e.g. / +grow_partition() { + local part="$1" + local mount_point="$2" + local disk partition_number fstype + + fstype="$(findmnt -n -o FSTYPE "$mount_point")" + + # Partitions we cannot grow are expected here: the sweep walks every mounted + # partition, and images ship an EFI system partition among them. + case "$fstype" in + ext2|ext3|ext4|xfs) ;; + *) + log "Filesystem '$fstype' on $part is not growable; skipping" + return 0 + ;; + esac + + disk="/dev/$(lsblk -no PKNAME "$part")" + partition_number="$(cat "/sys/class/block/$(basename "$part")/partition")" + + log "Growing $part ($fstype) on disk $disk, partition $partition_number, mounted at $mount_point" + + # growpart exits non-zero when the partition is already the last one and + # occupies the whole disk, so failures here are not fatal. + growpart "$disk" "$partition_number" || true + + case "$fstype" in + ext2|ext3|ext4) + resize2fs "$part" + ;; + xfs) + # XFS grows by mount point, not by device + xfs_growfs "$mount_point" + ;; + esac +} + +DISK="${1:-}" + +if [[ -n "$DISK" && ! -b "$DISK" ]]; then + log "Disk $DISK does not exist; nothing to grow" + exit 0 +fi + +wait_for_rw_root + +RC=0 +while read -r PART MOUNT_POINT; do + grow_partition "$PART" "$MOUNT_POINT" || RC=1 +done < <(list_mounted_partitions "$DISK") + +exit "$RC" diff --git a/exordos/images/exordos_base/install.sh b/exordos/images/exordos_base/install.sh index 6cfee69..bd40900 100644 --- a/exordos/images/exordos_base/install.sh +++ b/exordos/images/exordos_base/install.sh @@ -47,7 +47,8 @@ sudo sed -i 's/^preserve_hostname: false/preserve_hostname: false\napt_preserve_ sudo apt update sudo apt dist-upgrade -y sudo apt install -y build-essential python3-dev python3-venv \ - cloud-guest-utils irqbalance qemu-guest-agent libev-dev rsync parted j2cli vim + cloud-guest-utils irqbalance qemu-guest-agent libev-dev rsync parted j2cli vim \ + xfsprogs export UV_INSTALL_DIR="/usr/local/bin" curl --fail --show-error --location --progress-bar https://repo.exordos.com/uv/0.11.11/uv --output "${UV_INSTALL_DIR}/uv" @@ -79,15 +80,17 @@ sudo ln -sf "$AGENT_PATH/.venv/bin/exordos-universal-agent" "/usr/bin/exordos-un # Install stuff for bootstrap procedure and systemd services sudo mkdir -p "$WORK_DIR/bootstrap/scripts/" sudo cp "$IMG_ARTS_PATH/bootstrap.sh" "$WORK_DIR/bootstrap/" -sudo cp "$IMG_ARTS_PATH/root_autoresize.sh" "/usr/bin/" +sudo cp "$IMG_ARTS_PATH/exordos_autoresize.sh" "/usr/bin/" sudo cp "$IMG_ARTS_PATH/etc/systemd/exordos-bootstrap.service" $SYSTEMD_SERVICE_DIR -sudo cp "$IMG_ARTS_PATH/etc/systemd/exordos-root-autoresize.service" $SYSTEMD_SERVICE_DIR +sudo cp "$IMG_ARTS_PATH/etc/systemd/exordos-autoresize.service" $SYSTEMD_SERVICE_DIR +sudo cp "$IMG_ARTS_PATH/etc/systemd/exordos-autoresize@.service" $SYSTEMD_SERVICE_DIR sudo cp "$IMG_ARTS_PATH/etc/systemd/exordos-universal-agent.service" $SYSTEMD_SERVICE_DIR +sudo cp "$IMG_ARTS_PATH/etc/udev/90-exordos-autoresize.rules" /etc/udev/rules.d/ sudo mkdir -p "/usr/local/lib/exordos/" sudo cp -a "$IMG_ARTS_PATH/lib/." "/usr/local/lib/exordos/" # Enable exordos core services -sudo systemctl enable exordos-bootstrap exordos-root-autoresize exordos-universal-agent +sudo systemctl enable exordos-bootstrap exordos-autoresize exordos-universal-agent # Commented out because we don't need Alloy for now and we are going to change monitoring solution # Install Alloy diff --git a/exordos/images/exordos_base/root_autoresize.sh b/exordos/images/exordos_base/root_autoresize.sh deleted file mode 100755 index 67b04eb..0000000 --- a/exordos/images/exordos_base/root_autoresize.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2025 Genesis Corporation -# -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -set -eu -set -x -set -o pipefail - -# Detect root partition -ROOT_PART=$(findmnt -n -o SOURCE /) -PARTITION_NAME=$(echo "$ROOT_PART" | awk -F/ '{print $NF}') - -# Wait RW mode for the root partition -while [[ "$(findmnt / -o options -n | grep -E "^ro,|,ro,|,ro$")" != "" ]] -do - echo "Waiting for root partition to be in RW mode" - sleep 2 -done - -# Detect root device -for blk in $(ls /sys/block/ | grep -v loop); do - if [ -d "/sys/block/$blk/$PARTITION_NAME" ]; then - ROOT_DEV="/dev/$blk" - PARTITION_NUMBER=$(cat /sys/block/$blk/$PARTITION_NAME/partition) - break - fi -done - -if [ -z "$ROOT_DEV" ]; then - echo "Unable to detect root device" - exit 1 -fi - -echo "Detected root device: $ROOT_DEV" -echo "Detected partition name: $PARTITION_NAME" -echo "Detected partition number: $PARTITION_NUMBER" - -# Grow root partition and resize filesystem -growpart $ROOT_DEV $PARTITION_NUMBER || true -resize2fs /dev/$PARTITION_NAME