diff options
| author | Craig Jennings <c@cjennings.net> | 2026-04-26 01:08:50 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-04-26 01:08:50 -0500 |
| commit | 6b65665eca8a4b36b0b6eae4d761fccd7b4c1fc4 (patch) | |
| tree | 69cbf8bf23d4d97c89979909d2f96f799ced92e2 | |
| parent | bdda6b17c83e0e298c76bbaf146f082218389cd8 (diff) | |
| download | archangel-6b65665eca8a4b36b0b6eae4d761fccd7b4c1fc4.tar.gz archangel-6b65665eca8a4b36b0b6eae4d761fccd7b4c1fc4.zip | |
refactor: extract get_raid_level fzf preview into raid.sh helper
`get_raid_level()` carried a 98-line inline fzf `--preview` shell snippet that contained five nearly-parallel `case` branches emitting per-level preview text, plus three exported variables (`RAID_DISK_COUNT`, `RAID_TOTAL_GB`, `RAID_SMALLEST_GB`) just to pass values into the fzf preview subshell. The inline shell-in-shell had no syntax highlighting, no shellcheck on the inner snippet, and any edit to preview copy meant editing inside a single-quoted argument.
I extracted the per-level text into a new `raid_preview(level, disk_count, total_gb, smallest_gb)` helper in `lib/raid.sh`. It reuses the existing `raid_fault_tolerance` and `raid_usable_bytes` primitives for the data lines instead of redoing the arithmetic inline. That keeps the math in one place. The fzf `--preview` argument is now a one-liner that calls `raid_preview` with the sizing values, and the env-var exports are gone.
`export -f raid_preview raid_fault_tolerance raid_usable_bytes` makes the functions visible in fzf's preview subshell. I verified this against a fresh `bash -c` subshell, which is what fzf spawns internally.
`get_raid_level()` shrinks from 144 to 49 lines. Preview text is now bats-tested.
Added 8 unit tests across the 5 RAID levels (headline, fault-tolerance line, computed usable space), mixed-size handling for mirror (smallest disk, not average), unknown level returning 1 with empty output, and a sanity loop confirming every valid level produces non-empty output.
No behavior change. The preview pane shows the same text, the same level options, the same selected output. The pure logic in `lib/raid.sh` is unchanged.
| -rwxr-xr-x | installer/archangel | 108 | ||||
| -rw-r--r-- | installer/lib/raid.sh | 119 | ||||
| -rw-r--r-- | tests/unit/test_raid.bats | 69 |
3 files changed, 194 insertions, 102 deletions
diff --git a/installer/archangel b/installer/archangel index b3c232f..a40710b 100755 --- a/installer/archangel +++ b/installer/archangel @@ -373,114 +373,18 @@ get_raid_level() { 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 + # Make raid_preview and its dependencies available to fzf's preview + # subshell. The text and math live in lib/raid.sh so they're + # bats-tested and editor-friendly instead of buried in a + # single-quoted fzf argument. + export -f raid_preview raid_fault_tolerance raid_usable_bytes RAID_LEVEL=$(echo "$options" \ | fzf --height=20 --layout=reverse --border \ --header="Select RAID Level ($disk_count disks, ${total_gb}GB total)" \ - --preview=' - n=$RAID_DISK_COUNT - total=$RAID_TOTAL_GB - small=$RAID_SMALLEST_GB - - case {} in - mirror) - echo "MIRROR" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "All disks contain identical copies of data." - echo "Maximum redundancy - can survive loss of" - echo "all disks except one." - echo "" - echo "Redundancy: Can lose $((n-1)) of $n disks" - echo "Usable space: ~${small}GB (smallest disk)" - echo "Read speed: Fast (parallel reads)" - echo "Write speed: Normal" - echo "" - echo "Best for:" - echo " - Boot drives" - echo " - Critical data" - echo " - Maximum safety" - ;; - stripe) - echo "STRIPE (RAID0)" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "WARNING: NO REDUNDANCY!" - echo "Data is striped across all disks." - echo "ANY disk failure = ALL data lost!" - echo "" - echo "Redundancy: NONE" - echo "Usable space: ~${total}GB (all disks)" - echo "Read speed: Very fast" - echo "Write speed: Very fast" - echo "" - echo "Best for:" - echo " - Scratch/temp space" - echo " - Replaceable data" - echo " - Maximum performance" - ;; - raidz1) - usable=$(( (n-1) * small )) - echo "RAIDZ1 (Single Parity)" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "One disk worth of parity distributed" - echo "across all disks." - echo "" - echo "Redundancy: Can lose 1 disk" - echo "Usable space: ~${usable}GB ($((n-1)) of $n disks)" - echo "Read speed: Fast" - echo "Write speed: Good" - echo "" - echo "Best for:" - echo " - General storage" - echo " - Good balance of space/safety" - ;; - raidz2) - usable=$(( (n-2) * small )) - echo "RAIDZ2 (Double Parity)" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "Two disks worth of parity distributed" - echo "across all disks." - echo "" - echo "Redundancy: Can lose 2 disks" - echo "Usable space: ~${usable}GB ($((n-2)) of $n disks)" - echo "Read speed: Fast" - echo "Write speed: Good" - echo "" - echo "Best for:" - echo " - Large arrays (5+ disks)" - echo " - Important data" - ;; - raidz3) - usable=$(( (n-3) * small )) - echo "RAIDZ3 (Triple Parity)" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - echo "Three disks worth of parity distributed" - echo "across all disks." - echo "" - echo "Redundancy: Can lose 3 disks" - echo "Usable space: ~${usable}GB ($((n-3)) of $n disks)" - echo "Read speed: Fast" - echo "Write speed: Moderate" - echo "" - echo "Best for:" - echo " - Very large arrays (8+ disks)" - echo " - Archival storage" - ;; - esac - ' \ + --preview="raid_preview {} $disk_count $total_gb $smallest_gb" \ --preview-window=right:50%) - # Clean up exported variables - unset RAID_DISK_COUNT RAID_TOTAL_GB RAID_SMALLEST_GB - if [[ -z "$RAID_LEVEL" ]]; then error "No RAID level selected!" fi diff --git a/installer/lib/raid.sh b/installer/lib/raid.sh index 3e28177..6eccfa7 100644 --- a/installer/lib/raid.sh +++ b/installer/lib/raid.sh @@ -68,3 +68,122 @@ raid_fault_tolerance() { *) return 1 ;; esac } + +############################# +# Preview text +############################# + +# Print preview text for a single RAID level. Used by get_raid_level() +# in the fzf preview pane. Calls raid_fault_tolerance and +# raid_usable_bytes for the data lines so the math stays in one place. +# Numeric arguments are unit-agnostic — pass GB if you want GB out. +# +# Usage: raid_preview LEVEL DISK_COUNT TOTAL_GB SMALLEST_GB +# Returns 1 for unknown level (no output). +raid_preview() { + local level=$1 count=$2 total=$3 small=$4 + local tol usable + + tol=$(raid_fault_tolerance "$level" "$count") || return 1 + usable=$(raid_usable_bytes "$level" "$count" "$small" "$total") || return 1 + + case "$level" in + mirror) + cat <<EOF +MIRROR +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +All disks contain identical copies of data. +Maximum redundancy. Can survive loss of all +disks except one. + +Redundancy: Can lose $tol of $count disks +Usable space: ~${usable}GB (smallest disk) +Read speed: Fast (parallel reads) +Write speed: Normal + +Best for: + - Boot drives + - Critical data + - Maximum safety +EOF + ;; + stripe) + cat <<EOF +STRIPE (RAID0) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +WARNING: NO REDUNDANCY! +Data is striped across all disks. +ANY disk failure = ALL data lost! + +Redundancy: NONE +Usable space: ~${usable}GB (all disks) +Read speed: Very fast +Write speed: Very fast + +Best for: + - Scratch/temp space + - Replaceable data + - Maximum performance +EOF + ;; + raidz1) + cat <<EOF +RAIDZ1 (Single Parity) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +One disk worth of parity distributed +across all disks. + +Redundancy: Can lose $tol of $count disks +Usable space: ~${usable}GB ($((count - 1)) of $count disks) +Read speed: Fast +Write speed: Good + +Best for: + - General storage + - Good balance of space/safety +EOF + ;; + raidz2) + cat <<EOF +RAIDZ2 (Double Parity) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Two disks worth of parity distributed +across all disks. + +Redundancy: Can lose $tol of $count disks +Usable space: ~${usable}GB ($((count - 2)) of $count disks) +Read speed: Fast +Write speed: Good + +Best for: + - Large arrays (5+ disks) + - Important data +EOF + ;; + raidz3) + cat <<EOF +RAIDZ3 (Triple Parity) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Three disks worth of parity distributed +across all disks. + +Redundancy: Can lose $tol of $count disks +Usable space: ~${usable}GB ($((count - 3)) of $count disks) +Read speed: Fast +Write speed: Moderate + +Best for: + - Very large arrays (8+ disks) + - Archival storage +EOF + ;; + *) + return 1 + ;; + esac +} diff --git a/tests/unit/test_raid.bats b/tests/unit/test_raid.bats index 4e2f842..3972623 100644 --- a/tests/unit/test_raid.bats +++ b/tests/unit/test_raid.bats @@ -197,3 +197,72 @@ raidz3" ] run raid_fault_tolerance bogus 3 [ "$status" -eq 1 ] } + +############################# +# raid_preview +############################# +# Signature: raid_preview LEVEL DISK_COUNT TOTAL_GB SMALLEST_GB +# Returns 1 for unknown level. Output is preview text — assertions +# focus on structural pieces (headlines, computed numbers, key +# labels), not exact prose. + +@test "raid_preview mirror: headline + fault tolerance + smallest disk size" { + run raid_preview mirror 3 300 100 + [ "$status" -eq 0 ] + [[ "$output" == *"MIRROR"* ]] + [[ "$output" == *"Can lose 2 of 3 disks"* ]] + [[ "$output" == *"100"* ]] +} + +@test "raid_preview stripe: headline + no-redundancy warning + total size" { + run raid_preview stripe 2 200 100 + [ "$status" -eq 0 ] + [[ "$output" == *"STRIPE"* ]] + [[ "$output" == *"NO REDUNDANCY"* ]] + [[ "$output" == *"200"* ]] +} + +@test "raid_preview raidz1: headline + can-lose-1 + (n-1)*smallest size" { + run raid_preview raidz1 4 400 100 + [ "$status" -eq 0 ] + [[ "$output" == *"RAIDZ1"* ]] + [[ "$output" == *"Can lose 1 of 4 disks"* ]] + [[ "$output" == *"300"* ]] +} + +@test "raid_preview raidz2: headline + can-lose-2 + (n-2)*smallest size" { + run raid_preview raidz2 5 500 100 + [ "$status" -eq 0 ] + [[ "$output" == *"RAIDZ2"* ]] + [[ "$output" == *"Can lose 2 of 5 disks"* ]] + [[ "$output" == *"300"* ]] +} + +@test "raid_preview raidz3: headline + can-lose-3 + (n-3)*smallest size" { + run raid_preview raidz3 6 600 100 + [ "$status" -eq 0 ] + [[ "$output" == *"RAIDZ3"* ]] + [[ "$output" == *"Can lose 3 of 6 disks"* ]] + [[ "$output" == *"300"* ]] +} + +@test "raid_preview mirror with mixed-size disks honors smallest, not average" { + run raid_preview mirror 3 300 80 + [ "$status" -eq 0 ] + [[ "$output" == *"80"* ]] + [[ "$output" != *"100"* ]] +} + +@test "raid_preview unknown level returns 1 with empty output" { + run raid_preview bogus 3 300 100 + [ "$status" -eq 1 ] + [ -z "$output" ] +} + +@test "raid_preview every valid level produces non-empty output" { + for level in mirror stripe raidz1 raidz2 raidz3; do + run raid_preview "$level" 5 500 100 + [ "$status" -eq 0 ] + [ -n "$output" ] + done +} |
