400 lines
17 KiB
Bash
Executable File
400 lines
17 KiB
Bash
Executable File
#!/bin/sh
|
|
set -eu
|
|
|
|
project_root=${PROJECT_ROOT:-$(pwd)}
|
|
script_dir=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
|
|
fruix_cmd=$project_root/bin/fruix
|
|
os_template=${OS_TEMPLATE:-$script_dir/phase18-installer-target-operating-system.scm.in}
|
|
system_name=${SYSTEM_NAME:-phase18-target-operating-system}
|
|
store_dir=${STORE_DIR:-/frx/store}
|
|
installer_disk_capacity=${INSTALLER_DISK_CAPACITY:-16g}
|
|
installer_root_size=${INSTALLER_ROOT_SIZE:-14g}
|
|
target_disk_capacity=${TARGET_DISK_CAPACITY:-12g}
|
|
install_target_device=${INSTALL_TARGET_DEVICE:-/dev/vtbd1}
|
|
qemu_smp=${QEMU_SMP:-2}
|
|
installer_ssh_port=${INSTALLER_SSH_PORT:-10025}
|
|
target_ssh_port=${TARGET_SSH_PORT:-10026}
|
|
base_name=${BASE_NAME:-phase18-installer-target}
|
|
base_version_label=${BASE_VERSION_LABEL:-15.0-STABLE-installer-target}
|
|
base_release=${BASE_RELEASE:-15.0-STABLE}
|
|
base_branch=${BASE_BRANCH:-stable/15}
|
|
source_name=${SOURCE_NAME:-stable15-installer-target-source}
|
|
source_ref=${SOURCE_REF:-stable/15}
|
|
source_commit=${SOURCE_COMMIT:-332708a606f6bf0841c1d4a74c0d067f5640fe89}
|
|
declared_source_root=${DECLARED_SOURCE_ROOT:-/var/empty/fruix-unused-source-root-installer-target}
|
|
metadata_target=${METADATA_OUT:-}
|
|
root_authorized_key_file=${ROOT_AUTHORIZED_KEY_FILE:-$HOME/.ssh/id_ed25519.pub}
|
|
root_ssh_private_key_file=${ROOT_SSH_PRIVATE_KEY_FILE:-$HOME/.ssh/id_ed25519}
|
|
|
|
[ -x "$fruix_cmd" ] || {
|
|
echo "fruix command is not executable: $fruix_cmd" >&2
|
|
exit 1
|
|
}
|
|
[ -f "$os_template" ] || {
|
|
echo "missing operating-system template: $os_template" >&2
|
|
exit 1
|
|
}
|
|
[ -f "$root_authorized_key_file" ] || {
|
|
echo "missing root authorized key file: $root_authorized_key_file" >&2
|
|
exit 1
|
|
}
|
|
[ -f "$root_ssh_private_key_file" ] || {
|
|
echo "missing root SSH private key file: $root_ssh_private_key_file" >&2
|
|
exit 1
|
|
}
|
|
command -v qemu-system-x86_64 >/dev/null 2>&1 || {
|
|
echo "qemu-system-x86_64 is required" >&2
|
|
exit 1
|
|
}
|
|
[ -f /usr/local/share/edk2-qemu/QEMU_UEFI_CODE-x86_64.fd ] || {
|
|
echo "missing QEMU UEFI firmware" >&2
|
|
exit 1
|
|
}
|
|
|
|
cleanup=0
|
|
if [ -n "${WORKDIR:-}" ]; then
|
|
workdir=$WORKDIR
|
|
mkdir -p "$workdir"
|
|
else
|
|
workdir=$(mktemp -d /tmp/fruix-phase18-installer.XXXXXX)
|
|
cleanup=1
|
|
fi
|
|
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
|
|
cleanup=0
|
|
fi
|
|
|
|
target_os_file=$workdir/phase18-installer-target-operating-system.scm
|
|
installer_out=$workdir/installer.txt
|
|
metadata_file=$workdir/phase18-installer-environment-metadata.txt
|
|
installer_serial_log=$workdir/installer-serial.log
|
|
target_serial_log=$workdir/target-serial.log
|
|
installer_qemu_pidfile=$workdir/installer-qemu.pid
|
|
target_qemu_pidfile=$workdir/target-qemu.pid
|
|
installer_uefi_vars=$workdir/installer-vars.fd
|
|
target_uefi_vars=$workdir/target-vars.fd
|
|
installer_boot_image=$workdir/installer-boot.img
|
|
target_image=$workdir/installed-target.img
|
|
gpart_log=$workdir/gpart-show.txt
|
|
mnt_esp=$workdir/mnt-esp
|
|
mnt_root=$workdir/mnt-root
|
|
md_unit=
|
|
|
|
cleanup_workdir() {
|
|
if [ -f "$installer_qemu_pidfile" ]; then
|
|
sudo kill "$(sudo cat "$installer_qemu_pidfile")" >/dev/null 2>&1 || true
|
|
fi
|
|
if [ -f "$target_qemu_pidfile" ]; then
|
|
sudo kill "$(sudo cat "$target_qemu_pidfile")" >/dev/null 2>&1 || true
|
|
fi
|
|
if [ -n "$md_unit" ]; then
|
|
sudo umount "$mnt_esp" >/dev/null 2>&1 || true
|
|
sudo umount "$mnt_root" >/dev/null 2>&1 || true
|
|
sudo mdconfig -d -u "$md_unit" >/dev/null 2>&1 || true
|
|
fi
|
|
if [ "$cleanup" -eq 1 ]; then
|
|
rm -rf "$workdir" 2>/dev/null || sudo rm -rf "$workdir"
|
|
fi
|
|
}
|
|
trap cleanup_workdir EXIT INT TERM
|
|
|
|
root_authorized_key=$(tr -d '\n' < "$root_authorized_key_file")
|
|
sed \
|
|
-e "s|__BASE_NAME__|$base_name|g" \
|
|
-e "s|__BASE_VERSION_LABEL__|$base_version_label|g" \
|
|
-e "s|__BASE_RELEASE__|$base_release|g" \
|
|
-e "s|__BASE_BRANCH__|$base_branch|g" \
|
|
-e "s|__SOURCE_NAME__|$source_name|g" \
|
|
-e "s|__SOURCE_REF__|$source_ref|g" \
|
|
-e "s|__SOURCE_COMMIT__|$source_commit|g" \
|
|
-e "s|__DECLARED_SOURCE_ROOT__|$declared_source_root|g" \
|
|
-e "s|__ROOT_AUTHORIZED_KEY__|$root_authorized_key|g" \
|
|
"$os_template" > "$target_os_file"
|
|
|
|
cp /usr/local/share/edk2-qemu/QEMU_UEFI_VARS-x86_64.fd "$installer_uefi_vars"
|
|
cp /usr/local/share/edk2-qemu/QEMU_UEFI_VARS-x86_64.fd "$target_uefi_vars"
|
|
truncate -s "$target_disk_capacity" "$target_image"
|
|
mkdir -p "$mnt_esp" "$mnt_root"
|
|
|
|
action_env() {
|
|
sudo env \
|
|
HOME="$HOME" \
|
|
GUILE_AUTO_COMPILE=0 \
|
|
FRUIX_FREEBSD_BUILD_JOBS="${FRUIX_FREEBSD_BUILD_JOBS:-8}" \
|
|
GUIX_SOURCE_DIR="${GUIX_SOURCE_DIR:-$HOME/repos/guix}" \
|
|
GUILE_BIN="${GUILE_BIN:-/tmp/guile-freebsd-validate-install/bin/guile}" \
|
|
GUILE_EXTRA_PREFIX="${GUILE_EXTRA_PREFIX:-/tmp/guile-gnutls-freebsd-validate-install}" \
|
|
SHEPHERD_PREFIX="${SHEPHERD_PREFIX:-/tmp/shepherd-freebsd-validate-install}" \
|
|
"$@"
|
|
}
|
|
|
|
action_env "$fruix_cmd" system installer "$target_os_file" \
|
|
--system "$system_name" \
|
|
--store "$store_dir" \
|
|
--install-target-device "$install_target_device" \
|
|
--disk-capacity "$installer_disk_capacity" \
|
|
--root-size "$installer_root_size" >"$installer_out"
|
|
|
|
field() {
|
|
sed -n "s/^$1=//p" "$installer_out" | tail -n 1
|
|
}
|
|
|
|
image_store_path=$(field image_store_path)
|
|
installer_disk_image=$(field disk_image)
|
|
installer_esp_image=$(field esp_image)
|
|
installer_root_image=$(field root_image)
|
|
installer_closure_path=$(field installer_closure_path)
|
|
target_closure_path=$(field target_closure_path)
|
|
installer_host_name=$(field installer_host_name)
|
|
install_target_device_out=$(field install_target_device)
|
|
installer_state_path=$(field installer_state_path)
|
|
installer_log_path=$(field installer_log_path)
|
|
freebsd_source_kind_out=$(field freebsd_source_kind)
|
|
freebsd_source_ref_out=$(field freebsd_source_ref)
|
|
freebsd_source_commit_out=$(field freebsd_source_commit)
|
|
freebsd_source_file=$(field freebsd_source_file)
|
|
freebsd_source_materializations_file=$(field freebsd_source_materializations_file)
|
|
materialized_source_store_count=$(field materialized_source_store_count)
|
|
materialized_source_stores=$(field materialized_source_stores)
|
|
host_base_store_count=$(field host_base_store_count)
|
|
native_base_store_count=$(field native_base_store_count)
|
|
native_base_stores=$(field native_base_stores)
|
|
store_item_count=$(field store_item_count)
|
|
target_store_item_count=$(field target_store_item_count)
|
|
installer_store_item_count=$(field installer_store_item_count)
|
|
store_layout_file=$(field store_layout_file)
|
|
|
|
[ -d "$image_store_path" ] || { echo "missing installer image store path: $image_store_path" >&2; exit 1; }
|
|
[ -f "$installer_disk_image" ] || { echo "missing installer disk image: $installer_disk_image" >&2; exit 1; }
|
|
[ -f "$installer_esp_image" ] || { echo "missing installer ESP image: $installer_esp_image" >&2; exit 1; }
|
|
[ -f "$installer_root_image" ] || { echo "missing installer root image: $installer_root_image" >&2; exit 1; }
|
|
[ -n "$installer_closure_path" ] || { echo "missing installer closure path" >&2; exit 1; }
|
|
[ -n "$target_closure_path" ] || { echo "missing target closure path" >&2; exit 1; }
|
|
[ "$install_target_device_out" = "$install_target_device" ] || { echo "unexpected install target device: $install_target_device_out" >&2; exit 1; }
|
|
[ "$installer_host_name" = fruix-freebsd-installer ] || { echo "unexpected installer host name: $installer_host_name" >&2; exit 1; }
|
|
[ "$freebsd_source_kind_out" = git ] || { echo "unexpected source kind: $freebsd_source_kind_out" >&2; exit 1; }
|
|
[ "$freebsd_source_ref_out" = "$source_ref" ] || { echo "unexpected source ref: $freebsd_source_ref_out" >&2; exit 1; }
|
|
[ "$freebsd_source_commit_out" = "$source_commit" ] || { echo "unexpected source commit: $freebsd_source_commit_out" >&2; exit 1; }
|
|
[ "$materialized_source_store_count" = 1 ] || { echo "unexpected materialized source store count: $materialized_source_store_count" >&2; exit 1; }
|
|
[ "$host_base_store_count" = 0 ] || { echo "expected zero host base stores, got: $host_base_store_count" >&2; exit 1; }
|
|
[ "$native_base_store_count" = 3 ] || { echo "expected three native base stores, got: $native_base_store_count" >&2; exit 1; }
|
|
[ -f "$freebsd_source_file" ] || { echo "missing freebsd source file: $freebsd_source_file" >&2; exit 1; }
|
|
[ -f "$freebsd_source_materializations_file" ] || { echo "missing source materializations file: $freebsd_source_materializations_file" >&2; exit 1; }
|
|
[ -f "$store_layout_file" ] || { echo "missing store layout file: $store_layout_file" >&2; exit 1; }
|
|
case "$materialized_source_stores" in
|
|
/frx/store/*-freebsd-source-$source_name) : ;;
|
|
*) echo "unexpected materialized source store path: $materialized_source_stores" >&2; exit 1 ;;
|
|
esac
|
|
[ "$store_item_count" -ge "$target_store_item_count" ] || { echo "combined store item count smaller than target store item count" >&2; exit 1; }
|
|
[ "$installer_store_item_count" -ge 1 ] || { echo "expected installer store items" >&2; exit 1; }
|
|
|
|
cp "$installer_disk_image" "$installer_boot_image"
|
|
|
|
target_closure_base=$(basename "$target_closure_path")
|
|
installer_closure_base=$(basename "$installer_closure_path")
|
|
|
|
sudo qemu-system-x86_64 \
|
|
-machine q35,accel=tcg \
|
|
-cpu max \
|
|
-m 2048 \
|
|
-smp "$qemu_smp" \
|
|
-display none \
|
|
-serial "file:$installer_serial_log" \
|
|
-monitor none \
|
|
-pidfile "$installer_qemu_pidfile" \
|
|
-daemonize \
|
|
-drive if=pflash,format=raw,readonly=on,file=/usr/local/share/edk2-qemu/QEMU_UEFI_CODE-x86_64.fd \
|
|
-drive if=pflash,format=raw,file="$installer_uefi_vars" \
|
|
-drive if=virtio,format=raw,file="$installer_boot_image" \
|
|
-drive if=virtio,format=raw,file="$target_image" \
|
|
-netdev user,id=net0,hostfwd=tcp::${installer_ssh_port}-:22 \
|
|
-device virtio-net-pci,netdev=net0
|
|
|
|
installer_guest() {
|
|
ssh -p "$installer_ssh_port" -i "$root_ssh_private_key_file" \
|
|
-o BatchMode=yes \
|
|
-o StrictHostKeyChecking=no \
|
|
-o UserKnownHostsFile=/dev/null \
|
|
-o LogLevel=ERROR \
|
|
-o ConnectTimeout=5 \
|
|
root@127.0.0.1 "$@"
|
|
}
|
|
|
|
installer_ssh_reached=0
|
|
installer_state=missing
|
|
for attempt in $(jot 150 1 150); do
|
|
if installer_guest 'service sshd onestatus >/dev/null 2>&1' >/dev/null 2>&1; then
|
|
installer_ssh_reached=1
|
|
installer_state=$(installer_guest "cat '$installer_state_path' 2>/dev/null || echo missing")
|
|
[ "$installer_state" = done ] && break
|
|
fi
|
|
sleep 2
|
|
done
|
|
|
|
[ "$installer_ssh_reached" = 1 ] || { echo "installer environment never became reachable over SSH" >&2; exit 1; }
|
|
[ "$installer_state" = done ] || { echo "installer environment did not finish installation: $installer_state" >&2; exit 1; }
|
|
|
|
installer_run_current_system=$(installer_guest 'readlink /run/current-system')
|
|
installer_sshd_status=$(installer_guest 'service sshd onestatus >/dev/null 2>&1 && echo running || echo stopped')
|
|
installer_activate_log=$(installer_guest 'cat /var/log/fruix-activate.log 2>/dev/null || true' | tr '\n' ' ')
|
|
installer_log=$(installer_guest "cat '$installer_log_path' 2>/dev/null || true" | tr '\n' ' ')
|
|
|
|
[ "$installer_run_current_system" = "/frx/store/$installer_closure_base" ] || { echo "unexpected installer current-system target: $installer_run_current_system" >&2; exit 1; }
|
|
[ "$installer_sshd_status" = running ] || { echo "installer sshd is not running" >&2; exit 1; }
|
|
case "$installer_activate_log" in
|
|
*fruix-activate:done*) : ;;
|
|
*) echo "installer activation log does not show success" >&2; exit 1 ;;
|
|
esac
|
|
case "$installer_log" in
|
|
*fruix-installer:done*) : ;;
|
|
*) echo "installer log does not show completion" >&2; exit 1 ;;
|
|
esac
|
|
|
|
sudo kill "$(sudo cat "$installer_qemu_pidfile")" >/dev/null 2>&1 || true
|
|
rm -f "$installer_qemu_pidfile"
|
|
sleep 2
|
|
|
|
md=$(sudo mdconfig -a -t vnode -f "$target_image")
|
|
md_unit=${md#md}
|
|
sudo gpart show -lp "/dev/$md" >"$gpart_log"
|
|
esp_fstype=$(sudo fstyp "/dev/${md}p1")
|
|
root_fstype=$(sudo fstyp "/dev/${md}p2")
|
|
[ "$esp_fstype" = msdosfs ] || { echo "unexpected target ESP filesystem: $esp_fstype" >&2; exit 1; }
|
|
[ "$root_fstype" = ufs ] || { echo "unexpected target root filesystem: $root_fstype" >&2; exit 1; }
|
|
|
|
sudo mount -t msdosfs "/dev/${md}p1" "$mnt_esp"
|
|
sudo mount -t ufs -o ro "/dev/${md}p2" "$mnt_root"
|
|
|
|
[ -f "$mnt_esp/EFI/BOOT/BOOTX64.EFI" ] || { echo "missing EFI boot file on installed target" >&2; exit 1; }
|
|
target_run_current_system=$(readlink "$mnt_root/run/current-system")
|
|
target_boot_loader=$(readlink "$mnt_root/boot/loader")
|
|
install_metadata_host=$(cat "$mnt_root/var/lib/fruix/install.scm")
|
|
[ "$target_run_current_system" = "/frx/store/$target_closure_base" ] || { echo "unexpected target /run/current-system target: $target_run_current_system" >&2; exit 1; }
|
|
[ "$target_boot_loader" = /run/current-system/boot/loader ] || { echo "unexpected target boot loader link: $target_boot_loader" >&2; exit 1; }
|
|
[ -d "$mnt_root/frx/store/$target_closure_base" ] || { echo "installed target closure missing from target root" >&2; exit 1; }
|
|
case "$install_metadata_host" in
|
|
*"$target_closure_path"*) : ;;
|
|
*) echo "installed target metadata does not record target closure path" >&2; exit 1 ;;
|
|
esac
|
|
case "$install_metadata_host" in
|
|
*"$materialized_source_stores"*) : ;;
|
|
*) echo "installed target metadata does not record materialized source store" >&2; exit 1 ;;
|
|
esac
|
|
|
|
sudo umount "$mnt_esp"
|
|
sudo umount "$mnt_root"
|
|
sudo mdconfig -d -u "$md_unit"
|
|
md_unit=
|
|
|
|
sudo qemu-system-x86_64 \
|
|
-machine q35,accel=tcg \
|
|
-cpu max \
|
|
-m 2048 \
|
|
-smp "$qemu_smp" \
|
|
-display none \
|
|
-serial "file:$target_serial_log" \
|
|
-monitor none \
|
|
-pidfile "$target_qemu_pidfile" \
|
|
-daemonize \
|
|
-drive if=pflash,format=raw,readonly=on,file=/usr/local/share/edk2-qemu/QEMU_UEFI_CODE-x86_64.fd \
|
|
-drive if=pflash,format=raw,file="$target_uefi_vars" \
|
|
-drive if=virtio,format=raw,file="$target_image" \
|
|
-netdev user,id=net0,hostfwd=tcp::${target_ssh_port}-:22 \
|
|
-device virtio-net-pci,netdev=net0
|
|
|
|
target_guest() {
|
|
ssh -p "$target_ssh_port" -i "$root_ssh_private_key_file" \
|
|
-o BatchMode=yes \
|
|
-o StrictHostKeyChecking=no \
|
|
-o UserKnownHostsFile=/dev/null \
|
|
-o LogLevel=ERROR \
|
|
-o ConnectTimeout=5 \
|
|
root@127.0.0.1 "$@"
|
|
}
|
|
|
|
for attempt in $(jot 120 1 120); do
|
|
if target_guest 'service sshd onestatus >/dev/null 2>&1' >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
sleep 2
|
|
done
|
|
|
|
target_run_current_system_guest=$(target_guest 'readlink /run/current-system')
|
|
target_shepherd_status=$(target_guest '/usr/local/etc/rc.d/fruix-shepherd onestatus >/dev/null 2>&1 && echo running || echo stopped')
|
|
target_sshd_status=$(target_guest 'service sshd onestatus >/dev/null 2>&1 && echo running || echo stopped')
|
|
target_install_metadata_guest=$(target_guest 'cat /var/lib/fruix/install.scm')
|
|
target_activate_log=$(target_guest 'cat /var/log/fruix-activate.log 2>/dev/null || true' | tr '\n' ' ')
|
|
|
|
[ "$target_run_current_system_guest" = "/frx/store/$target_closure_base" ] || { echo "unexpected booted target current-system: $target_run_current_system_guest" >&2; exit 1; }
|
|
[ "$target_shepherd_status" = running ] || { echo "fruix-shepherd is not running in booted target" >&2; exit 1; }
|
|
[ "$target_sshd_status" = running ] || { echo "sshd is not running in booted target" >&2; exit 1; }
|
|
case "$target_install_metadata_guest" in
|
|
*"$target_closure_path"*) : ;;
|
|
*) echo "booted target metadata does not record target closure path" >&2; exit 1 ;;
|
|
esac
|
|
case "$target_install_metadata_guest" in
|
|
*"$materialized_source_stores"*) : ;;
|
|
*) echo "booted target metadata does not record materialized source store" >&2; exit 1 ;;
|
|
esac
|
|
case "$target_activate_log" in
|
|
*fruix-activate:done*) : ;;
|
|
*) echo "booted target activation log does not show success" >&2; exit 1 ;;
|
|
esac
|
|
|
|
cat >"$metadata_file" <<EOF
|
|
workdir=$workdir
|
|
target_os_file=$target_os_file
|
|
installer_image_store_path=$image_store_path
|
|
installer_disk_image=$installer_disk_image
|
|
installer_boot_image=$installer_boot_image
|
|
installer_disk_capacity=$installer_disk_capacity
|
|
installer_root_size=$installer_root_size
|
|
target_image=$target_image
|
|
target_disk_capacity=$target_disk_capacity
|
|
install_target_device=$install_target_device
|
|
qemu_smp=$qemu_smp
|
|
freebsd_source_kind=$freebsd_source_kind_out
|
|
freebsd_source_ref=$freebsd_source_ref_out
|
|
freebsd_source_commit=$freebsd_source_commit_out
|
|
freebsd_source_file=$freebsd_source_file
|
|
freebsd_source_materializations_file=$freebsd_source_materializations_file
|
|
materialized_source_store_count=$materialized_source_store_count
|
|
materialized_source_store=$materialized_source_stores
|
|
installer_closure_path=$installer_closure_path
|
|
target_closure_path=$target_closure_path
|
|
native_base_store_count=$native_base_store_count
|
|
native_base_stores=$native_base_stores
|
|
store_item_count=$store_item_count
|
|
target_store_item_count=$target_store_item_count
|
|
installer_store_item_count=$installer_store_item_count
|
|
installer_state_path=$installer_state_path
|
|
installer_log_path=$installer_log_path
|
|
installer_state=$installer_state
|
|
installer_run_current_system=$installer_run_current_system
|
|
installer_sshd_status=$installer_sshd_status
|
|
installer_serial_log=$installer_serial_log
|
|
target_esp_fstype=$esp_fstype
|
|
target_root_fstype=$root_fstype
|
|
gpart_log=$gpart_log
|
|
target_run_current_system=$target_run_current_system_guest
|
|
target_shepherd_status=$target_shepherd_status
|
|
target_sshd_status=$target_sshd_status
|
|
target_serial_log=$target_serial_log
|
|
installer_environment_boot=ok
|
|
installer_environment_install=ok
|
|
installed_target_boot=ok
|
|
EOF
|
|
|
|
if [ -n "$metadata_target" ]; then
|
|
mkdir -p "$(dirname "$metadata_target")"
|
|
cp "$metadata_file" "$metadata_target"
|
|
fi
|
|
|
|
printf 'PASS phase18-installer-environment\n'
|
|
printf 'Work directory: %s\n' "$workdir"
|
|
printf 'Metadata file: %s\n' "$metadata_file"
|
|
if [ -n "$metadata_target" ]; then
|
|
printf 'Copied metadata to: %s\n' "$metadata_target"
|
|
fi
|
|
printf '%s\n' '--- metadata ---'
|
|
cat "$metadata_file"
|