aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbuild.sh35
-rw-r--r--installer/lib/config.sh22
-rw-r--r--tests/unit/test_config.bats58
3 files changed, 82 insertions, 33 deletions
diff --git a/build.sh b/build.sh
index 6dbdef0..566a2a7 100755
--- a/build.sh
+++ b/build.sh
@@ -447,33 +447,14 @@ EOF
info "Setting root password for live ISO..."
# Generate password hash
PASS_HASH=$(openssl passwd -6 "$LIVE_ROOT_PASSWORD")
-# Modify the existing shadow file's root entry (don't replace entire file)
-# The releng template has multiple accounts; replacing breaks the file
-if [[ -f "$PROFILE_DIR/airootfs/etc/shadow" ]]; then
- sed -i "s|^root:[^:]*:|root:${PASS_HASH}:|" "$PROFILE_DIR/airootfs/etc/shadow"
-else
- # Fallback: create complete shadow file if it doesn't exist
- cat > "$PROFILE_DIR/airootfs/etc/shadow" << EOF
-root:${PASS_HASH}:19000:0:99999:7:::
-bin:!*:19000::::::
-daemon:!*:19000::::::
-mail:!*:19000::::::
-ftp:!*:19000::::::
-http:!*:19000::::::
-nobody:!*:19000::::::
-dbus:!*:19000::::::
-systemd-coredump:!*:19000::::::
-systemd-network:!*:19000::::::
-systemd-oom:!*:19000::::::
-systemd-journal-remote:!*:19000::::::
-systemd-resolve:!*:19000::::::
-systemd-timesync:!*:19000::::::
-tss:!*:19000::::::
-uuidd:!*:19000::::::
-polkitd:!*:19000::::::
-avahi:!*:19000::::::
-EOF
-fi
+# Modify the existing shadow file's root entry (don't replace the whole
+# file — the releng template ships /etc/shadow with multiple accounts and
+# rewriting it from scratch would drop them). The profile is always copied
+# fresh from releng above, so the file is present; if it's missing, that
+# copy is broken — fail loudly rather than silently rebuilding a stale list.
+[[ -f "$PROFILE_DIR/airootfs/etc/shadow" ]] \
+ || error "Expected shadow file missing: $PROFILE_DIR/airootfs/etc/shadow (releng profile copy broken?)"
+sed -i "s|^root:[^:]*:|root:${PASS_HASH}:|" "$PROFILE_DIR/airootfs/etc/shadow"
chmod 400 "$PROFILE_DIR/airootfs/etc/shadow"
# Allow root SSH login with password (for testing)
diff --git a/installer/lib/config.sh b/installer/lib/config.sh
index 3ba2bb3..ed54e36 100644
--- a/installer/lib/config.sh
+++ b/installer/lib/config.sh
@@ -116,20 +116,30 @@ check_config() {
validate_config() {
local errors=0
- [[ -z "$HOSTNAME" ]] && { warn "HOSTNAME not set"; ((errors++)); }
- [[ -z "$TIMEZONE" ]] && { warn "TIMEZONE not set"; ((errors++)); }
- [[ ${#SELECTED_DISKS[@]} -eq 0 ]] && { warn "No disks selected"; ((errors++)); }
- [[ -z "$ROOT_PASSWORD" ]] && { warn "ROOT_PASSWORD not set"; ((errors++)); }
+ [[ -z "$HOSTNAME" ]] && { warn "HOSTNAME not set"; ((++errors)); }
+ [[ -z "$TIMEZONE" ]] && { warn "TIMEZONE not set"; ((++errors)); }
+ [[ ${#SELECTED_DISKS[@]} -eq 0 ]] && { warn "No disks selected"; ((++errors)); }
+ [[ -z "$ROOT_PASSWORD" ]] && { warn "ROOT_PASSWORD not set"; ((++errors)); }
# Validate disks exist
for disk in "${SELECTED_DISKS[@]}"; do
- [[ -b "$disk" ]] || { warn "Disk not found: $disk"; ((errors++)); }
+ [[ -b "$disk" ]] || { warn "Disk not found: $disk"; ((++errors)); }
done
# Validate timezone
if [[ -n "$TIMEZONE" && ! -f "/usr/share/zoneinfo/$TIMEZONE" ]]; then
warn "Invalid timezone: $TIMEZONE"
- ((errors++))
+ ((++errors))
+ fi
+
+ # Validate the RAID level against the selected disk count. The
+ # interactive path only offers levels valid for the count, so this
+ # guards the unattended config, where RAID_LEVEL is set by hand and
+ # can name a level the disk count can't support. raid_is_valid treats
+ # an empty level on a single disk (no RAID) as valid.
+ if ! raid_is_valid "$RAID_LEVEL" "${#SELECTED_DISKS[@]}"; then
+ warn "Invalid RAID_LEVEL '$RAID_LEVEL' for ${#SELECTED_DISKS[@]} disk(s)"
+ ((++errors))
fi
if [[ $errors -gt 0 ]]; then
diff --git a/tests/unit/test_config.bats b/tests/unit/test_config.bats
index af23e4a..4169c5e 100644
--- a/tests/unit/test_config.bats
+++ b/tests/unit/test_config.bats
@@ -5,6 +5,8 @@ setup() {
# shellcheck disable=SC1091
source "${BATS_TEST_DIRNAME}/../../installer/lib/common.sh"
# shellcheck disable=SC1091
+ source "${BATS_TEST_DIRNAME}/../../installer/lib/raid.sh"
+ # shellcheck disable=SC1091
source "${BATS_TEST_DIRNAME}/../../installer/lib/config.sh"
}
@@ -93,6 +95,62 @@ EOF
[[ "$output" == *"4 error"* ]]
}
+@test "validate_config under set -e reports every error, not just the first" {
+ # Reproduces the monolith's call structure: `set -e` is active and
+ # validate_config is invoked as the final command of an && list. A
+ # post-increment that returns the pre-increment value (0 on the first
+ # error) trips set -e and aborts the function after one warning. This
+ # test runs outside bats' `run` shield (which sets +e) so the real
+ # accumulate-and-report behavior is exercised.
+ run bash -c '
+ set -e
+ source "'"${BATS_TEST_DIRNAME}"'/../../installer/lib/common.sh"
+ source "'"${BATS_TEST_DIRNAME}"'/../../installer/lib/raid.sh"
+ source "'"${BATS_TEST_DIRNAME}"'/../../installer/lib/config.sh"
+ HOSTNAME=""; TIMEZONE=""; SELECTED_DISKS=(); ROOT_PASSWORD=""
+ UNATTENDED=true
+ [[ "$UNATTENDED" == true ]] && validate_config
+ '
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"HOSTNAME not set"* ]]
+ [[ "$output" == *"TIMEZONE not set"* ]]
+ [[ "$output" == *"No disks selected"* ]]
+ [[ "$output" == *"ROOT_PASSWORD not set"* ]]
+ [[ "$output" == *"4 error"* ]]
+}
+
+@test "validate_config rejects a RAID_LEVEL invalid for the disk count" {
+ HOSTNAME=h
+ TIMEZONE=UTC
+ ROOT_PASSWORD=x
+ SELECTED_DISKS=(/dev/sda /dev/sdb)
+ RAID_LEVEL=raidz1
+ run validate_config
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"Invalid RAID_LEVEL"* ]]
+ [[ "$output" == *"raidz1"* ]]
+}
+
+@test "validate_config accepts a RAID_LEVEL valid for the disk count" {
+ HOSTNAME=h
+ TIMEZONE=UTC
+ ROOT_PASSWORD=x
+ SELECTED_DISKS=(/dev/sda /dev/sdb /dev/sdc)
+ RAID_LEVEL=raidz1
+ run validate_config
+ [[ "$output" != *"Invalid RAID_LEVEL"* ]]
+}
+
+@test "validate_config accepts an empty RAID_LEVEL for a single disk" {
+ HOSTNAME=h
+ TIMEZONE=UTC
+ ROOT_PASSWORD=x
+ SELECTED_DISKS=(/dev/sda)
+ RAID_LEVEL=""
+ run validate_config
+ [[ "$output" != *"Invalid RAID_LEVEL"* ]]
+}
+
@test "validate_config rejects an invalid timezone" {
HOSTNAME="h"
TIMEZONE="Not/A_Real_Zone_xyz"