diff --git a/scripts/setup/machine-setup/lib/base.sh b/scripts/setup/machine-setup/lib/base.sh index 17b9d6fc..b74fddb3 100644 --- a/scripts/setup/machine-setup/lib/base.sh +++ b/scripts/setup/machine-setup/lib/base.sh @@ -40,6 +40,19 @@ PM="" # apt | pacman | dnf | brew ARCH="" # amd64 | arm64, normalised — upstream tarballs disagree on spelling IS_WSL=false +# What this box is FOR. Asked once in pre-flight and consulted by the steps +# afterwards, because several of them have a different right answer per role and +# no way to work it out on their own: +# +# homelab a machine you physically control on a network you own +# vps rented, public IP, someone else's DHCP and console +# dev a laptop or desktop you sit at +# +# Set MACHINE_ROLE in the environment to answer it ahead of time — hence the +# :- default rather than a plain assignment, which would wipe what the caller +# passed in before ask_machine_role ever looked at it. +MACHINE_ROLE="${MACHINE_ROLE:-}" + # ----------------------------------------------------------------------------- # Output # ----------------------------------------------------------------------------- @@ -207,3 +220,87 @@ detect_os() { [[ -n "$OS" ]] || fail "Could not identify this distribution (no readable /etc/os-release)." [[ -n "$OS_NAME" ]] || OS_NAME="$OS${OS_VERSION:+ $OS_VERSION}" } + +# ----------------------------------------------------------------------------- +# Machine role +# ----------------------------------------------------------------------------- + +# The interface packets actually leave by, which is not always the first one up. +default_iface() { + ip route get 8.8.8.8 2>/dev/null | awk '{for (i = 1; i <= NF; i++) if ($i == "dev") {print $(i + 1); exit}}' +} + +# A default for the question below — a suggestion the user confirms, never a +# decision taken on their behalf. +# +# The signal that separates homelab from vps is whether this machine's own +# address is in RFC1918 space. That is a better test than asking whether it is +# virtualised, because a homelab is very often a VM on Proxmox and would be +# misread as rented; and it is the same fact that decides most of what the role +# is consulted for — whether the address is yours to pin, and whether the box is +# exposed to the open internet. +guess_machine_role() { + # Nothing here is a server. It is the machine you are sitting at. + if [[ "$OS" == "macos" ]]; then + echo dev + return + fi + + # A graphical session means someone sits at this one too. + if [[ -n "${DISPLAY:-}" ]] || systemctl list-unit-files 2>/dev/null | grep -qE '^(gdm3?|sddm|lightdm)\.service'; then + echo dev + return + fi + + local ip + ip="$(ip -4 addr show "$(default_iface)" 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -1)" + case "$ip" in + 10.* | 192.168.* | 172.1[6-9].* | 172.2[0-9].* | 172.3[01].*) echo homelab ;; + "") echo homelab ;; # no address to judge by: assume the safer of the two + *) echo vps ;; + esac +} + +# Ask what this machine is, unless the environment already said. +# +# Asked in pre-flight rather than at the point of use so that the run knows its +# own shape before it starts: the steps that care are spread from swap through to +# the firewall, and being asked "is this a VPS?" for the fourth time halfway down +# a provisioning run is how people start answering without reading. +ask_machine_role() { + if [[ -n "$MACHINE_ROLE" ]]; then + case "$MACHINE_ROLE" in + homelab | vps | dev) return ;; + *) fail "MACHINE_ROLE must be homelab, vps or dev — got '$MACHINE_ROLE'" ;; + esac + fi + + local guess choice + guess="$(guess_machine_role)" + + echo "" + info "What is this machine?" + echo " [1] homelab — yours, on a network you control" + echo " [2] vps — rented, public IP, provider's DHCP and console" + echo " [3] dev — a laptop or desktop you sit at" + echo "" + + case "$guess" in + homelab) choice=1 ;; + vps) choice=2 ;; + dev) choice=3 ;; + esac + + prompt_value MACHINE_ROLE_CHOICE "Pick a number" "$choice" + + case "$MACHINE_ROLE_CHOICE" in + 1 | homelab) MACHINE_ROLE=homelab ;; + 2 | vps) MACHINE_ROLE=vps ;; + 3 | dev) MACHINE_ROLE=dev ;; + *) fail "Not one of the options: '$MACHINE_ROLE_CHOICE'" ;; + esac +} + +# Convenience for the steps that branch on it. +is_role() { [[ "$MACHINE_ROLE" == "$1" ]]; } +is_server() { [[ "$MACHINE_ROLE" == "homelab" || "$MACHINE_ROLE" == "vps" ]]; } diff --git a/scripts/setup/machine-setup/machine-setup.sh b/scripts/setup/machine-setup/machine-setup.sh index bd2fa1cc..1d409429 100755 --- a/scripts/setup/machine-setup/machine-setup.sh +++ b/scripts/setup/machine-setup/machine-setup.sh @@ -49,6 +49,9 @@ case "$PM" in *) fail "${OS_NAME} uses ${PM}, which this script does not implement yet — apt-based systems only, so far." ;; esac +ask_machine_role +info "Role: ${MACHINE_ROLE}" + if [[ -f "$PROGRESS_FILE" ]]; then DONE_COUNT=$(wc -l < "$PROGRESS_FILE") echo -e "${YELLOW} Resuming — $DONE_COUNT step(s) already completed${NC}"