Bridge Shepherd to FreeBSD services
This commit is contained in:
324
tests/shepherd/run-freebsd-shepherd-bridge-prototype.sh
Executable file
324
tests/shepherd/run-freebsd-shepherd-bridge-prototype.sh
Executable file
@@ -0,0 +1,324 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)
|
||||
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}
|
||||
shepherd_bin=$shepherd_prefix/bin/shepherd
|
||||
herd_bin=$shepherd_prefix/bin/herd
|
||||
metadata_target=${METADATA_OUT:-}
|
||||
|
||||
if [ ! -x "$guile_bin" ]; then
|
||||
echo "Guile binary is not executable: $guile_bin" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ensure_built() {
|
||||
if [ ! -d "$guile_extra_prefix/share/guile/site" ] || \
|
||||
! GUILE_LOAD_PATH="$guile_extra_prefix/share/guile/site/3.0${GUILE_LOAD_PATH:+:$GUILE_LOAD_PATH}" \
|
||||
GUILE_LOAD_COMPILED_PATH="$guile_extra_prefix/lib/guile/3.0/site-ccache${GUILE_LOAD_COMPILED_PATH:+:$GUILE_LOAD_COMPILED_PATH}" \
|
||||
GUILE_EXTENSIONS_PATH="$guile_extra_prefix/lib/guile/3.0/extensions${GUILE_EXTENSIONS_PATH:+:$GUILE_EXTENSIONS_PATH}" \
|
||||
LD_LIBRARY_PATH="$guile_extra_prefix/lib:/tmp/guile-freebsd-validate-install/lib:/usr/local/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \
|
||||
"$guile_bin" -c '(catch #t (lambda () (use-modules (fibers)) (display "ok") (newline)) (lambda _ (display "missing") (newline)))' | grep -qx ok; then
|
||||
METADATA_OUT= ENV_OUT= "$repo_root/tests/shepherd/build-local-guile-fibers.sh"
|
||||
fi
|
||||
|
||||
if [ ! -x "$shepherd_bin" ] || [ ! -x "$herd_bin" ]; then
|
||||
METADATA_OUT= ENV_OUT= GUILE_EXTRA_PREFIX="$guile_extra_prefix" "$repo_root/tests/shepherd/build-local-shepherd.sh"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_built
|
||||
|
||||
run_root() {
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
"$@"
|
||||
else
|
||||
sudo "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
guile_bindir=$(CDPATH= cd -- "$(dirname "$guile_bin")" && pwd)
|
||||
guile_prefix=$(CDPATH= cd -- "$guile_bindir/.." && pwd)
|
||||
ld_library_path=$guile_extra_prefix/lib:$guile_prefix/lib:/usr/local/lib
|
||||
guile_load_path=$repo_root/modules:$guile_extra_prefix/share/guile/site/3.0
|
||||
guile_load_compiled_path=$guile_extra_prefix/lib/guile/3.0/site-ccache
|
||||
guile_extensions_path=$guile_extra_prefix/lib/guile/3.0/extensions
|
||||
|
||||
run_root_env() {
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
env \
|
||||
LD_LIBRARY_PATH="$ld_library_path" \
|
||||
GUILE_LOAD_PATH="$guile_load_path" \
|
||||
GUILE_LOAD_COMPILED_PATH="$guile_load_compiled_path" \
|
||||
GUILE_EXTENSIONS_PATH="$guile_extensions_path" \
|
||||
"$@"
|
||||
else
|
||||
sudo env \
|
||||
LD_LIBRARY_PATH="$ld_library_path" \
|
||||
GUILE_LOAD_PATH="$guile_load_path" \
|
||||
GUILE_LOAD_COMPILED_PATH="$guile_load_compiled_path" \
|
||||
GUILE_EXTENSIONS_PATH="$guile_extensions_path" \
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup=0
|
||||
if [ -n "${WORKDIR:-}" ]; then
|
||||
workdir=$WORKDIR
|
||||
mkdir -p "$workdir"
|
||||
else
|
||||
workdir=$(mktemp -d /tmp/freebsd-shepherd-bridge.XXXXXX)
|
||||
cleanup=1
|
||||
fi
|
||||
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
|
||||
cleanup=0
|
||||
fi
|
||||
|
||||
chmod 0755 "$workdir"
|
||||
config_file=$workdir/bridge-config.scm
|
||||
socket_file=$workdir/shepherd.sock
|
||||
pid_file=$workdir/shepherd.pid
|
||||
shepherd_log=$workdir/shepherd.log
|
||||
shepherd_stdout=$workdir/shepherd.out
|
||||
metadata_file=$workdir/freebsd-shepherd-bridge-metadata.txt
|
||||
status_file=$workdir/bridge-target.status
|
||||
mock_rc_log=$workdir/mock-rc.log
|
||||
mount_point=$workdir/tmpfs-mount
|
||||
home_dir=$workdir/home
|
||||
mock_rc_name=fruix_bridge_rc_$$
|
||||
mock_rc_script=/usr/local/etc/rc.d/$mock_rc_name
|
||||
loopback_alias=127.251.$(($$ % 200 + 1)).1
|
||||
|
||||
pick_free_id() {
|
||||
for candidate in $(jot 100 36000 36099); do
|
||||
if ! getent passwd "$candidate" >/dev/null 2>&1 && ! getent group "$candidate" >/dev/null 2>&1; then
|
||||
echo "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
temp_id=$(pick_free_id)
|
||||
temp_user=fruixbridge$temp_id
|
||||
temp_group=$temp_user
|
||||
|
||||
cleanup_workdir() {
|
||||
run_root_env "$herd_bin" -s "$socket_file" stop root >/dev/null 2>&1 || true
|
||||
run_root rm -f "$mock_rc_script"
|
||||
run_root /sbin/ifconfig lo0 -alias "$loopback_alias" >/dev/null 2>&1 || true
|
||||
if mount | grep -F "on $mount_point " >/dev/null 2>&1; then
|
||||
run_root /sbin/umount "$mount_point" >/dev/null 2>&1 || true
|
||||
fi
|
||||
run_root /usr/sbin/pw userdel "$temp_user" -r >/dev/null 2>&1 || true
|
||||
run_root /usr/sbin/pw groupdel "$temp_group" >/dev/null 2>&1 || true
|
||||
if [ "$cleanup" -eq 1 ]; then
|
||||
run_root rm -rf "$workdir"
|
||||
fi
|
||||
}
|
||||
trap cleanup_workdir EXIT INT TERM
|
||||
|
||||
cat >"$workdir/mock-rc-template.in" <<'EOF'
|
||||
#!/bin/sh
|
||||
. /etc/rc.subr
|
||||
name=__RC_NAME__
|
||||
start_cmd="__RC_START_FN__"
|
||||
stop_cmd="__RC_STOP_FN__"
|
||||
__RC_START_FN__()
|
||||
{
|
||||
echo started >> __RC_LOG__
|
||||
}
|
||||
__RC_STOP_FN__()
|
||||
{
|
||||
echo stopped >> __RC_LOG__
|
||||
}
|
||||
load_rc_config $name
|
||||
run_rc_command "$1"
|
||||
EOF
|
||||
mock_start_fn=${mock_rc_name}_start
|
||||
mock_stop_fn=${mock_rc_name}_stop
|
||||
sed \
|
||||
-e "s|__RC_NAME__|$mock_rc_name|g" \
|
||||
-e "s|__RC_START_FN__|$mock_start_fn|g" \
|
||||
-e "s|__RC_STOP_FN__|$mock_stop_fn|g" \
|
||||
-e "s|__RC_LOG__|$mock_rc_log|g" \
|
||||
"$workdir/mock-rc-template.in" | run_root tee "$mock_rc_script" >/dev/null
|
||||
run_root chmod +x "$mock_rc_script"
|
||||
|
||||
cat >"$config_file" <<EOF
|
||||
(use-modules (fruix shepherd freebsd)
|
||||
(shepherd service)
|
||||
(shepherd support))
|
||||
|
||||
(register-services
|
||||
(list
|
||||
(freebsd-rc-service '(bridge-rc) "$mock_rc_name"
|
||||
#:documentation "Run a temporary FreeBSD rc.d script through Shepherd.")
|
||||
(freebsd-loopback-alias-service '(bridge-network) "$loopback_alias"
|
||||
#:requirement '(bridge-rc)
|
||||
#:documentation "Configure a temporary loopback alias through Shepherd.")
|
||||
(freebsd-tmpfs-service '(bridge-filesystem) "$mount_point"
|
||||
#:requirement '(bridge-network)
|
||||
#:size "1m"
|
||||
#:mode "0750"
|
||||
#:documentation "Mount a temporary tmpfs filesystem through Shepherd.")
|
||||
(freebsd-user-group-service '(bridge-account) "$temp_user" "$temp_group"
|
||||
#:uid $temp_id
|
||||
#:gid $temp_id
|
||||
#:home "$home_dir"
|
||||
#:requirement '(bridge-filesystem)
|
||||
#:documentation "Create a temporary user and group through Shepherd.")
|
||||
(service '(bridge-target)
|
||||
#:documentation "Top-level FreeBSD bridge target."
|
||||
#:requirement '(bridge-account)
|
||||
#:start (lambda _ #t)
|
||||
#:stop (lambda _ #f)
|
||||
#:respawn? #f)))
|
||||
|
||||
(start-service (lookup-service 'bridge-target))
|
||||
EOF
|
||||
|
||||
run_root_env "$shepherd_bin" -I -s "$socket_file" -c "$config_file" --pid="$pid_file" -l "$shepherd_log" >"$shepherd_stdout" 2>&1 &
|
||||
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||
if [ -f "$pid_file" ] && [ -S "$socket_file" ]; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if [ ! -f "$pid_file" ] || [ ! -S "$socket_file" ]; then
|
||||
echo "Shepherd bridge prototype did not become ready" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run_root_env "$herd_bin" -s "$socket_file" status bridge-target >"$status_file"
|
||||
case $(cat "$status_file") in
|
||||
*"It is running"*) target_running=yes ;;
|
||||
*) target_running=no ;;
|
||||
esac
|
||||
[ "$target_running" = yes ] || {
|
||||
echo "Bridge target is not reported as running" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if grep -qx started "$mock_rc_log"; then
|
||||
rc_started=yes
|
||||
else
|
||||
rc_started=no
|
||||
fi
|
||||
if ifconfig lo0 | grep -F "$loopback_alias" >/dev/null 2>&1; then
|
||||
alias_present=yes
|
||||
else
|
||||
alias_present=no
|
||||
fi
|
||||
if mount | grep -F "on $mount_point " >/dev/null 2>&1; then
|
||||
tmpfs_mounted=yes
|
||||
else
|
||||
tmpfs_mounted=no
|
||||
fi
|
||||
tmpfs_mode=$(stat -f '%Sp' "$mount_point")
|
||||
if id "$temp_user" >/dev/null 2>&1; then
|
||||
user_present=yes
|
||||
else
|
||||
user_present=no
|
||||
fi
|
||||
if getent group "$temp_group" >/dev/null 2>&1; then
|
||||
group_present=yes
|
||||
else
|
||||
group_present=no
|
||||
fi
|
||||
|
||||
[ "$rc_started" = yes ] || { echo "Mock rc service did not start" >&2; exit 1; }
|
||||
[ "$alias_present" = yes ] || { echo "Loopback alias was not created" >&2; exit 1; }
|
||||
[ "$tmpfs_mounted" = yes ] || { echo "tmpfs mount was not created" >&2; exit 1; }
|
||||
[ "$tmpfs_mode" = 'drwxr-x---' ] || { echo "Unexpected tmpfs mode: $tmpfs_mode" >&2; exit 1; }
|
||||
[ "$user_present" = yes ] || { echo "Temporary user was not created" >&2; exit 1; }
|
||||
[ "$group_present" = yes ] || { echo "Temporary group was not created" >&2; exit 1; }
|
||||
|
||||
run_root_env "$herd_bin" -s "$socket_file" stop root >/dev/null || true
|
||||
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||
[ ! -f "$pid_file" ] && break
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if grep -qx stopped "$mock_rc_log"; then
|
||||
rc_stopped=yes
|
||||
else
|
||||
rc_stopped=no
|
||||
fi
|
||||
if ifconfig lo0 | grep -F "$loopback_alias" >/dev/null 2>&1; then
|
||||
alias_removed=no
|
||||
else
|
||||
alias_removed=yes
|
||||
fi
|
||||
if mount | grep -F "on $mount_point " >/dev/null 2>&1; then
|
||||
tmpfs_unmounted=no
|
||||
else
|
||||
tmpfs_unmounted=yes
|
||||
fi
|
||||
if id "$temp_user" >/dev/null 2>&1; then
|
||||
user_removed=no
|
||||
else
|
||||
user_removed=yes
|
||||
fi
|
||||
if getent group "$temp_group" >/dev/null 2>&1; then
|
||||
group_removed=no
|
||||
else
|
||||
group_removed=yes
|
||||
fi
|
||||
case $(cat "$shepherd_stdout") in
|
||||
*"System lacks support for 'signalfd'; using fallback mechanism."*) signalfd_fallback=yes ;;
|
||||
*) signalfd_fallback=no ;;
|
||||
esac
|
||||
|
||||
[ "$rc_stopped" = yes ] || { echo "Mock rc service did not stop" >&2; exit 1; }
|
||||
[ "$alias_removed" = yes ] || { echo "Loopback alias was not removed" >&2; exit 1; }
|
||||
[ "$tmpfs_unmounted" = yes ] || { echo "tmpfs mount was not removed" >&2; exit 1; }
|
||||
[ "$user_removed" = yes ] || { echo "Temporary user still exists after stop" >&2; exit 1; }
|
||||
[ "$group_removed" = yes ] || { echo "Temporary group still exists after stop" >&2; exit 1; }
|
||||
|
||||
cat >"$metadata_file" <<EOF
|
||||
workdir=$workdir
|
||||
config_file=$config_file
|
||||
socket_file=$socket_file
|
||||
pid_file=$pid_file
|
||||
shepherd_log=$shepherd_log
|
||||
shepherd_stdout=$shepherd_stdout
|
||||
status_file=$status_file
|
||||
mock_rc_name=$mock_rc_name
|
||||
mock_rc_script=$mock_rc_script
|
||||
mock_rc_log=$mock_rc_log
|
||||
loopback_alias=$loopback_alias
|
||||
mount_point=$mount_point
|
||||
tmpfs_mode=$tmpfs_mode
|
||||
temp_user=$temp_user
|
||||
temp_group=$temp_group
|
||||
temp_id=$temp_id
|
||||
target_running=$target_running
|
||||
rc_started=$rc_started
|
||||
alias_present=$alias_present
|
||||
tmpfs_mounted=$tmpfs_mounted
|
||||
user_present=$user_present
|
||||
group_present=$group_present
|
||||
rc_stopped=$rc_stopped
|
||||
alias_removed=$alias_removed
|
||||
tmpfs_unmounted=$tmpfs_unmounted
|
||||
user_removed=$user_removed
|
||||
group_removed=$group_removed
|
||||
signalfd_fallback=$signalfd_fallback
|
||||
EOF
|
||||
|
||||
if [ -n "$metadata_target" ]; then
|
||||
mkdir -p "$(dirname "$metadata_target")"
|
||||
cp "$metadata_file" "$metadata_target"
|
||||
fi
|
||||
|
||||
printf 'PASS freebsd-shepherd-bridge-prototype\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"
|
||||
Reference in New Issue
Block a user