diff options
| author | Craig Jennings <c@cjennings.net> | 2026-04-12 23:58:01 -0400 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-04-12 23:58:01 -0400 |
| commit | 19f4624749228fcbe385d1edf1d2542c036440ff (patch) | |
| tree | 961e815d2226178345f5ec7ec7cb82f23da34379 /installer | |
| parent | 863ceeac8fdb10258a58d35bcee6874097fffc88 (diff) | |
| download | archangel-19f4624749228fcbe385d1edf1d2542c036440ff.tar.gz archangel-19f4624749228fcbe385d1edf1d2542c036440ff.zip | |
refactor: extract pure RAID logic to lib/raid.sh with bats coverage
Peel the testable pieces of get_raid_level() out of the 1600-line
installer monolith into installer/lib/raid.sh:
- raid_valid_levels_for_count(count) — replaces the inline option-list
builder in get_raid_level()
- raid_is_valid(level, count) — useful for unattended-config validation
- raid_usable_bytes(level, count, smallest, total) — usable-space math
- raid_fault_tolerance(level, count) — max tolerable disk failures
archangel now sources lib/raid.sh and uses raid_valid_levels_for_count
for the fzf option list. Fzf preview subshell still inlines its own
usable-bytes arithmetic (calling exported lib functions across preview
subshells is fragile; left for a later pass).
30 bats tests in tests/unit/test_raid.bats cover the full enumeration
table, every valid/invalid level-vs-count combo from 2 to 5 disks,
mixed-size mirror, and unknown-level error paths. make test: 53/53.
Diffstat (limited to 'installer')
| -rwxr-xr-x | installer/archangel | 11 | ||||
| -rw-r--r-- | installer/lib/raid.sh | 70 |
2 files changed, 75 insertions, 6 deletions
diff --git a/installer/archangel b/installer/archangel index 615829d..d1831cf 100755 --- a/installer/archangel +++ b/installer/archangel @@ -29,6 +29,7 @@ source "$SCRIPT_DIR/lib/common.sh" source "$SCRIPT_DIR/lib/config.sh" source "$SCRIPT_DIR/lib/disk.sh" source "$SCRIPT_DIR/lib/btrfs.sh" +source "$SCRIPT_DIR/lib/raid.sh" ############################# # Configuration @@ -368,18 +369,16 @@ get_raid_level() { local total_gb=$((total_bytes / 1073741824)) local smallest_gb=$((smallest_bytes / 1073741824)) - # Build options based on disk count - local options="mirror\nstripe" - [[ $disk_count -ge 3 ]] && options+="\nraidz1" - [[ $disk_count -ge 4 ]] && options+="\nraidz2" - [[ $disk_count -ge 5 ]] && options+="\nraidz3" + # Build options based on disk count (pure logic → lib/raid.sh) + local options + options=$(raid_valid_levels_for_count "$disk_count") # Export variables for preview subshell export RAID_DISK_COUNT=$disk_count export RAID_TOTAL_GB=$total_gb export RAID_SMALLEST_GB=$smallest_gb - RAID_LEVEL=$(echo -e "$options" \ + RAID_LEVEL=$(echo "$options" \ | fzf --height=20 --layout=reverse --border \ --header="Select RAID Level ($disk_count disks, ${total_gb}GB total)" \ --preview=' diff --git a/installer/lib/raid.sh b/installer/lib/raid.sh new file mode 100644 index 0000000..3e28177 --- /dev/null +++ b/installer/lib/raid.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# raid.sh - Pure RAID-level logic (testable, no I/O). +# Source after common.sh. + +############################# +# Valid-level enumeration +############################# + +# Print valid RAID levels for a given disk count, one per line. +# Count <2: nothing printed (single disk = no RAID). +# Count 2: mirror, stripe +# Count 3+: + raidz1 +# Count 4+: + raidz2 +# Count 5+: + raidz3 +raid_valid_levels_for_count() { + local count=$1 + [[ $count -lt 2 ]] && return 0 + echo mirror + echo stripe + [[ $count -ge 3 ]] && echo raidz1 + [[ $count -ge 4 ]] && echo raidz2 + [[ $count -ge 5 ]] && echo raidz3 + return 0 +} + +# Return 0 if level is valid for the given disk count, 1 otherwise. +# Empty level with count 1 is valid (no RAID). +raid_is_valid() { + local level=$1 count=$2 + if [[ $count -le 1 ]]; then + [[ -z "$level" ]] + return + fi + raid_valid_levels_for_count "$count" | grep -qxF "$level" +} + +############################# +# Usable-space computation +############################# + +# Print usable bytes for a level given disk count, smallest-disk bytes, +# and total bytes across all disks. Writes nothing and returns 1 for +# unknown levels. +# +# Usage: raid_usable_bytes LEVEL COUNT SMALLEST_BYTES TOTAL_BYTES +raid_usable_bytes() { + local level=$1 count=$2 smallest=$3 total=$4 + case "$level" in + mirror) echo "$smallest" ;; + stripe) echo "$total" ;; + raidz1) echo $(( (count - 1) * smallest )) ;; + raidz2) echo $(( (count - 2) * smallest )) ;; + raidz3) echo $(( (count - 3) * smallest )) ;; + *) return 1 ;; + esac +} + +# Print fault-tolerance (max number of disks that can fail) for a level +# at the given disk count. Unknown level → return 1. +raid_fault_tolerance() { + local level=$1 count=$2 + case "$level" in + mirror) echo $(( count - 1 )) ;; + stripe) echo 0 ;; + raidz1) echo 1 ;; + raidz2) echo 2 ;; + raidz3) echo 3 ;; + *) return 1 ;; + esac +} |
