145 lines
5.5 KiB
Bash
145 lines
5.5 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
# scripts/onboard/steps/00-preflight.sh — READ-ONLY remote node discovery
|
||
|
|
#
|
||
|
|
# Collects facts from the remote node and prints:
|
||
|
|
# 1. A human-readable report block
|
||
|
|
# 2. A machine-readable YAML snippet ready to paste into hosts/<node>/node.yaml
|
||
|
|
#
|
||
|
|
# NO mutations are performed on the remote host.
|
||
|
|
# Depends on: lib/common.sh (sourced by orchestrator), lib/remote.sh (sourced here)
|
||
|
|
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
STEP_NAME="00-preflight"
|
||
|
|
|
||
|
|
# remote.sh is sourced here so individual steps can also be run standalone
|
||
|
|
# (when REPO_ROOT is in the environment).
|
||
|
|
: "${REPO_ROOT:?REPO_ROOT is not set — run via onboard.sh}"
|
||
|
|
# shellcheck source=../lib/remote.sh
|
||
|
|
source "${REPO_ROOT}/scripts/onboard/lib/remote.sh"
|
||
|
|
|
||
|
|
step "[$STEP_NAME] Collecting facts from ${ONBOARD_SSH_USER}@${ONBOARD_SSH_HOST} (read-only)"
|
||
|
|
|
||
|
|
# ── gather all facts in a single SSH session ──────────────────────────────────
|
||
|
|
raw=$(rrun bash -s <<'REMOTE'
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
# arch / bitness
|
||
|
|
arch=$(uname -m)
|
||
|
|
bits=$(getconf LONG_BIT)
|
||
|
|
|
||
|
|
# RAM (kB → MB)
|
||
|
|
mem_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
||
|
|
mem_mb=$(( mem_kb / 1024 ))
|
||
|
|
|
||
|
|
# disk root
|
||
|
|
disk_root=$(df -h / | awk 'NR==2{print $2" total, "$3" used, "$4" free ("$5" used)"}')
|
||
|
|
|
||
|
|
# docker
|
||
|
|
docker_present=false
|
||
|
|
docker_info=""
|
||
|
|
if command -v docker >/dev/null 2>&1; then
|
||
|
|
docker_present=true
|
||
|
|
docker_info=$(docker info --format '{{.ServerVersion}}' 2>/dev/null || echo "unknown")
|
||
|
|
fi
|
||
|
|
|
||
|
|
# tailscale
|
||
|
|
tailscale_present=false
|
||
|
|
tailscale_status=""
|
||
|
|
if command -v tailscale >/dev/null 2>&1; then
|
||
|
|
tailscale_present=true
|
||
|
|
tailscale_status=$(tailscale status --json 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('BackendState','unknown'))" 2>/dev/null || tailscale status 2>/dev/null | head -1 || echo "unknown")
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Magic Mirror runtime detection
|
||
|
|
mm_runtime="none"
|
||
|
|
if systemctl is-active --quiet MagicMirror 2>/dev/null || systemctl is-active --quiet magicmirror 2>/dev/null; then
|
||
|
|
mm_runtime="systemd"
|
||
|
|
elif command -v pm2 >/dev/null 2>&1 && pm2 list 2>/dev/null | grep -qi "MagicMirror"; then
|
||
|
|
mm_runtime="pm2"
|
||
|
|
elif pgrep -fa "MagicMirror" >/dev/null 2>&1; then
|
||
|
|
mm_runtime="process"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# swap
|
||
|
|
swap_current="none"
|
||
|
|
if command -v swapon >/dev/null 2>&1; then
|
||
|
|
swap_lines=$(swapon --show --noheadings 2>/dev/null || true)
|
||
|
|
if [[ -n "$swap_lines" ]]; then
|
||
|
|
swap_current="$swap_lines"
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
if command -v zramctl >/dev/null 2>&1; then
|
||
|
|
zram_lines=$(zramctl --noheadings 2>/dev/null || true)
|
||
|
|
[[ -n "$zram_lines" ]] && swap_current="${swap_current:+$swap_current; }zram: $zram_lines"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# hostname / os
|
||
|
|
hostname=$(hostname -f 2>/dev/null || hostname)
|
||
|
|
os_pretty=$(grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d= -f2 | tr -d '"' || echo "unknown")
|
||
|
|
|
||
|
|
cat <<EOF
|
||
|
|
ARCH=$arch
|
||
|
|
BITS=$bits
|
||
|
|
MEM_MB=$mem_mb
|
||
|
|
DISK_ROOT=$disk_root
|
||
|
|
DOCKER_PRESENT=$docker_present
|
||
|
|
DOCKER_VERSION=$docker_info
|
||
|
|
TAILSCALE_PRESENT=$tailscale_present
|
||
|
|
TAILSCALE_STATUS=$tailscale_status
|
||
|
|
MM_RUNTIME=$mm_runtime
|
||
|
|
SWAP_CURRENT=$swap_current
|
||
|
|
HOSTNAME=$hostname
|
||
|
|
OS=$os_pretty
|
||
|
|
EOF
|
||
|
|
REMOTE
|
||
|
|
)
|
||
|
|
|
||
|
|
# ── parse key=value output ────────────────────────────────────────────────────
|
||
|
|
_val() { echo "$raw" | grep "^${1}=" | head -1 | cut -d= -f2-; }
|
||
|
|
|
||
|
|
arch=$(_val ARCH)
|
||
|
|
bits=$(_val BITS)
|
||
|
|
mem_mb=$(_val MEM_MB)
|
||
|
|
disk_root=$(_val DISK_ROOT)
|
||
|
|
docker_present=$(_val DOCKER_PRESENT)
|
||
|
|
docker_version=$(_val DOCKER_VERSION)
|
||
|
|
tailscale_present=$(_val TAILSCALE_PRESENT)
|
||
|
|
tailscale_status=$(_val TAILSCALE_STATUS)
|
||
|
|
mm_runtime=$(_val MM_RUNTIME)
|
||
|
|
swap_current=$(_val SWAP_CURRENT)
|
||
|
|
remote_hostname=$(_val HOSTNAME)
|
||
|
|
os_pretty=$(_val OS)
|
||
|
|
|
||
|
|
# ── human-readable report ─────────────────────────────────────────────────────
|
||
|
|
echo ""
|
||
|
|
echo "┌─────────────────────────────────────────────────────┐"
|
||
|
|
printf "│ Preflight report: %-33s│\n" "${ONBOARD_SSH_HOST}"
|
||
|
|
echo "├─────────────────────────────────────────────────────┤"
|
||
|
|
printf "│ hostname : %-35s│\n" "$remote_hostname"
|
||
|
|
printf "│ OS : %-35s│\n" "$os_pretty"
|
||
|
|
printf "│ arch : %-35s│\n" "${arch} (${bits}-bit)"
|
||
|
|
printf "│ RAM : %-35s│\n" "${mem_mb} MB"
|
||
|
|
printf "│ disk / : %-35s│\n" "$disk_root"
|
||
|
|
printf "│ docker : %-35s│\n" "${docker_present} (v${docker_version})"
|
||
|
|
printf "│ tailscale : %-35s│\n" "${tailscale_present} / ${tailscale_status}"
|
||
|
|
printf "│ MagicMirror : %-35s│\n" "$mm_runtime"
|
||
|
|
printf "│ swap : %-35s│\n" "${swap_current:-none}"
|
||
|
|
echo "└─────────────────────────────────────────────────────┘"
|
||
|
|
echo ""
|
||
|
|
|
||
|
|
# ── machine-readable YAML snippet ────────────────────────────────────────────
|
||
|
|
echo "# ── paste into hosts/${NODE_NAME,,}/node.yaml ──"
|
||
|
|
cat <<YAML
|
||
|
|
hardware:
|
||
|
|
arch: ${arch}
|
||
|
|
ram_mb: ${mem_mb}
|
||
|
|
swap: ${swap_current:-none}
|
||
|
|
docker_present: ${docker_present}
|
||
|
|
docker_version: "${docker_version}"
|
||
|
|
tailscale_status: "${tailscale_status}"
|
||
|
|
mm_runtime: ${mm_runtime}
|
||
|
|
YAML
|
||
|
|
|
||
|
|
log "[$STEP_NAME] done — no changes made to remote host"
|