prole/prole.sh
chrisfu ec8a4e98af k3s: Fix installation hangs, CA mismatches, and arm64 networking
- Implement proactive CA hash verification and automated repair for mismatches

- Ensure agents prioritize discovered server tokens over stale vault values

- Fix K3s service hangs with explicit stop and killall before reinstall

- Add Retropie/Pi networking fixes (WiFi power save, wlan0 priority)

- Pin pre-staged images to stable, architecture-aware versions (arm64)

- Remove obsolete init-port-forwards and prole.cfg sync tasks

- Update k8s manifests and installer core logic with new tests
2026-02-16 23:30:27 -08:00

216 lines
6.5 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# prole.sh - Unified launcher for Prole infrastructure and installer
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "${ROOT_DIR}"
# Defaults
VERBOSE=0
DEBUG=0
LOG=0
COVERAGE=0
SILENT=0
RESET=0
EXTRA_ARGS=()
usage() {
echo "Usage: $0 [options] [command] [command-options]"
echo
echo "Options:"
echo " -l, --log Create a datestamped logfile in prole/logs including all stdout/stderr"
echo " -v, --verbose Pass a verbose switch to all functions called by ansible and install.py"
echo " -d, --debug Enable a debug logfile in prole/logs with high verbosity from install.py"
echo " -c, --coverage Create a coverage report for the run of install.py"
echo " -s, --silent Run unattended install (passes -S to install.py)"
echo " -r, --reset Perform a reset before action (site/deploy/install)"
echo
echo "Commands:"
echo " install Run the ncurses-based terminal installer"
echo " ansible Run ansible-playbook via ansible.sh"
echo " site Shortcut for running the site deployment"
echo " reset Shortcut for k3s factory reset (full clear)"
echo " deploy Full end-to-end: site run, and silent install (reset optional)"
echo
echo "Any other arguments are passed directly to ansible.sh"
}
# Parse global options
while [[ $# -gt 0 ]]; do
case "$1" in
-l|--log|--logs) LOG=1; shift ;;
-v|--verbose) VERBOSE=1; shift ;;
-d|--debug) DEBUG=1; shift ;;
-c|--coverage) COVERAGE=1; shift ;;
-s|--silent) SILENT=1; shift ;;
-r|--reset) RESET=1; shift ;;
-h|--help) usage; exit 0 ;;
install|ansible|site|reset|deploy) break ;;
*) EXTRA_ARGS+=("$1"); shift ;;
esac
done
if [[ $# -eq 0 && ${#EXTRA_ARGS[@]} -eq 0 ]]; then
usage
exit 1
fi
CMD="${1:-ansible}"
[[ $# -gt 0 ]] && shift
# Prepare command arguments
CMD_ARGS=()
if [[ "${VERBOSE}" -eq 1 ]]; then
CMD_ARGS+=("--verbose")
fi
# We separate arguments that are only for install.py
INSTALL_ONLY_ARGS=()
if [[ "${DEBUG}" -eq 1 ]]; then
INSTALL_ONLY_ARGS+=("--debug")
fi
if [[ "${SILENT}" -eq 1 ]]; then
if [[ ! -f "${ROOT_DIR}/conf/prole.cfg" ]]; then
echo "Error: --silent requires ${ROOT_DIR}/conf/prole.cfg" >&2
exit 1
fi
INSTALL_ONLY_ARGS+=("-S")
fi
if [[ "${RESET}" -eq 1 ]]; then
INSTALL_ONLY_ARGS+=("--reset")
fi
CMD_ARGS+=("${EXTRA_ARGS[@]}" "$@")
# Helper for logging
run_with_log() {
local logfile="$1"
shift
mkdir -p "$(dirname "${logfile}")"
echo "Logging to ${logfile}"
"$@" 2>&1 | tee "${logfile}"
}
# Run command
TS="$(date +%Y%m%d-%H%M%S)"
# Prep for Ansible truthfulness
if [[ -f "${ROOT_DIR}/etc/sync-prole-cfg.py" ]]; then
# We use the same vault pass logic as ansible.sh if available
export PYTHONPATH="${PYTHONPATH:-}:${ROOT_DIR}"
python3 "${ROOT_DIR}/etc/sync-prole-cfg.py" || true
fi
case "${CMD}" in
install)
LOGFILE="${ROOT_DIR}/logs/install-${TS}.log"
PY_CMD=("python3")
if [[ "${COVERAGE}" -eq 1 ]]; then
PY_CMD=("coverage" "run" "--source=installer")
fi
PY_CMD+=("${ROOT_DIR}/install.py" "--no-gui")
if [[ "${LOG}" -eq 1 ]]; then
run_with_log "${LOGFILE}" "${PY_CMD[@]}" "${INSTALL_ONLY_ARGS[@]}" "${CMD_ARGS[@]}"
elif [[ "${COVERAGE}" -eq 1 ]]; then
"${PY_CMD[@]}" "${INSTALL_ONLY_ARGS[@]}" "${CMD_ARGS[@]}"
else
exec "${PY_CMD[@]}" "${INSTALL_ONLY_ARGS[@]}" "${CMD_ARGS[@]}"
fi
if [[ "${COVERAGE}" -eq 1 ]]; then
echo "Generating coverage report..."
coverage report -m
coverage html
echo "HTML report generated in ${ROOT_DIR}/htmlcov/index.html"
fi
;;
ansible)
LOGFILE="${ROOT_DIR}/logs/ansible-${TS}.log"
if [[ "${LOG}" -eq 1 ]]; then
run_with_log "${LOGFILE}" "${ROOT_DIR}/ansible.sh" "${CMD_ARGS[@]}"
else
"${ROOT_DIR}/ansible.sh" "${CMD_ARGS[@]}"
fi
# SYNC AFTER ANSIBLE
if [[ -f "${ROOT_DIR}/etc/sync-prole-cfg.py" ]]; then
python3 "${ROOT_DIR}/etc/sync-prole-cfg.py" || true
fi
;;
site)
LOGFILE="${ROOT_DIR}/logs/site-${TS}.log"
(
set -e
if [[ "${RESET}" -eq 1 ]]; then
echo "Resetting k3s before site run..."
"${ROOT_DIR}/ansible.sh" -p infrastructure/playbooks/k3s_delete.yml "${CMD_ARGS[@]}"
fi
if [[ "${LOG}" -eq 1 ]]; then
run_with_log "${LOGFILE}" "${ROOT_DIR}/ansible.sh" -p infrastructure/playbooks/site.yml "${CMD_ARGS[@]}"
else
"${ROOT_DIR}/ansible.sh" -p infrastructure/playbooks/site.yml "${CMD_ARGS[@]}"
fi
# SYNC AFTER SITE
if [[ -f "${ROOT_DIR}/etc/sync-prole-cfg.py" ]]; then
python3 "${ROOT_DIR}/etc/sync-prole-cfg.py" || true
fi
)
;;
reset)
LOGFILE="${ROOT_DIR}/logs/reset-${TS}.log"
if [[ "${LOG}" -eq 1 ]]; then
run_with_log "${LOGFILE}" "${ROOT_DIR}/ansible.sh" -p infrastructure/playbooks/k3s_delete.yml "${CMD_ARGS[@]}"
else
"${ROOT_DIR}/ansible.sh" -p infrastructure/playbooks/k3s_delete.yml "${CMD_ARGS[@]}"
fi
# SYNC AFTER RESET (to clear token)
if [[ -f "${ROOT_DIR}/etc/sync-prole-cfg.py" ]]; then
python3 "${ROOT_DIR}/etc/sync-prole-cfg.py" || true
fi
;;
deploy)
LOGFILE="${ROOT_DIR}/logs/deploy-${TS}.log"
echo "Starting full end-to-end deployment..."
(
set -e
if [[ "${RESET}" -eq 1 ]]; then
echo "Step 1/3: Resetting k3s (full)..."
"${ROOT_DIR}/ansible.sh" -p infrastructure/playbooks/k3s_delete.yml "${CMD_ARGS[@]}"
else
echo "Step 1/3: Skipping k3s reset (use -r to force reset)"
fi
echo "Step 2/3: Running site deployment..."
"${ROOT_DIR}/ansible.sh" -p infrastructure/playbooks/site.yml "${CMD_ARGS[@]}"
if [[ -f "${ROOT_DIR}/etc/sync-prole-cfg.py" ]]; then
python3 "${ROOT_DIR}/etc/sync-prole-cfg.py" || true
fi
echo "Step 3/3: Running silent installer..."
# INSTALL_ONLY_ARGS includes -S and --reset if flags were set
python3 "${ROOT_DIR}/install.py" "--no-gui" "-c" "${ROOT_DIR}/conf/prole.cfg" "${INSTALL_ONLY_ARGS[@]}" "${CMD_ARGS[@]}"
echo "Deployment completed successfully!"
) | tee "${LOGFILE}"
;;
*)
# Default to ansible if command not matched
LOGFILE="${ROOT_DIR}/logs/${CMD}-${TS}.log"
if [[ "${LOG}" -eq 1 ]]; then
run_with_log "${LOGFILE}" "${ROOT_DIR}/ansible.sh" "${CMD}" "${CMD_ARGS[@]}"
else
"${ROOT_DIR}/ansible.sh" "${CMD}" "${CMD_ARGS[@]}"
fi
# SYNC AFTER ANSIBLE
if [[ -f "${ROOT_DIR}/etc/sync-prole-cfg.py" ]]; then
python3 "${ROOT_DIR}/etc/sync-prole-cfg.py" || true
fi
;;
esac