hub v0.28.0 + host-install v1.2.0: settings→Configuration, online install, vmid auto-detect
Part A (hub): move the global-floor + Day-0-artifacts cards from the Customers
page to the Configuration tab; routes → /configuration/{global-floor,artifacts};
redirects + flashes to /configuration. Customers page back to list + Add.
Part B: online setup command on the customer page (download-then-run, passphrase
at prompt, not templated); serve /scripts/ from the website (sparse-checkout +
nginx location) so felhom.eu/scripts/felhom-host-install.sh resolves; script
passphrase prompt reads < /dev/tty (works for pipe-to-bash too).
Part C (script): --vmid auto-detect — default 9201 in use + no --force → pick the
next free id from pct+qm and confirm; explicit --vmid stays die-unless-force.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,25 @@
|
||||
# Felhom scripts — Changelog
|
||||
|
||||
## felhom-host-install.sh v1.2.0 — /dev/tty passphrase read + vmid auto-detect (2026-07-01)
|
||||
|
||||
Two operator-experience fixes so a colleague can install online (via the hub's new "Option 1: Online
|
||||
install" one-liner) and onto a host that already runs a guest at 9201.
|
||||
|
||||
- **Passphrase prompt reads from `/dev/tty`, not stdin** (`read_passphrase`). `read -rsp … < /dev/tty`
|
||||
makes the no-echo prompt work regardless of how stdin is wired — both download-then-run **and**
|
||||
`curl … | sudo bash` (where stdin is the pipe). Strictly more correct; the `--passphrase-file` path is
|
||||
unchanged. The passphrase is still never on argv / in logs / in the state file.
|
||||
- **VMID auto-detect (`--vmid` now optional-smart).** New `VMID_EXPLICIT` flag (set by `--vmid`). The
|
||||
pre-flight vmid guard now determines "in use" against the **`pct list` + `qm list`** id-set (LXC and
|
||||
VMs share the id space — more complete than the old `pct status`, which only knew LXC):
|
||||
- **explicit `--vmid`** → unchanged deterministic behavior: die if the id is in use unless `--force`
|
||||
(destructive over-provision).
|
||||
- **default 9201, in use, no `--force`** → **auto-pick the next free id** (scan upward from 9201 over
|
||||
the used-set) and **ask to confirm** from the terminal (`read … < /dev/tty`, `[y/N]`); proceed on
|
||||
yes, `die "no free vmid confirmed"` otherwise. Never a silent auto-pick.
|
||||
- **default 9201 + `--force`** → over-provision 9201 (destructive) without prompting, as before.
|
||||
- New helpers `used_vmids` / `_vmid_in_use` / `next_free_vmid`. `--vmid` help text + `usage()` updated.
|
||||
|
||||
## felhom-host-install.sh v1.1.0 — self-install the agent + fetch the golden from Gitea (2026-06-28)
|
||||
|
||||
The script now **installs the agent itself** (the last big manual Day-0 prerequisite is gone). It
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
# Options:
|
||||
# --mode provision|dr provision (Day-0, default) | dr (10D stub — not impl.)
|
||||
# --hub-url URL default https://hub.felhom.eu
|
||||
# --vmid N guest VMID to provision (default 9201)
|
||||
# --vmid N guest VMID to provision. Default 9201; if omitted and 9201 is already
|
||||
# in use, the script auto-picks the next free id (pct+qm) and asks to
|
||||
# confirm. An EXPLICIT --vmid stays deterministic (dies unless --force).
|
||||
# --golden VOLID golden archive volid (default: newest vzdump of the
|
||||
# golden build VMID on the archive storage; else fetched
|
||||
# from Gitea per the hub artifact manifest)
|
||||
@@ -73,7 +75,7 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_VERSION="1.1.0"
|
||||
SCRIPT_VERSION="1.2.0"
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Logging (mirrors felhom-controller/scripts/docker-setup.sh)
|
||||
@@ -96,6 +98,7 @@ CUSTOMER_ID=""
|
||||
MODE="provision"
|
||||
HUB_URL="https://hub.felhom.eu"
|
||||
VMID="9201"
|
||||
VMID_EXPLICIT=false # set true when --vmid is given; gates the auto-pick-a-free-vmid behavior
|
||||
GOLDEN_VOLID=""
|
||||
GOLDEN_VMID="9100"
|
||||
ARCHIVE_STORAGE="local"
|
||||
@@ -150,12 +153,35 @@ ART_GOLDEN_SHA=""
|
||||
#-------------------------------------------------------------------------------
|
||||
# Helpers
|
||||
#-------------------------------------------------------------------------------
|
||||
usage() { sed -n '2,61p' "$0" | sed 's/^# \{0,1\}//'; exit 0; }
|
||||
usage() { sed -n '2,65p' "$0" | sed 's/^# \{0,1\}//'; exit 0; }
|
||||
|
||||
run() { # simple (no pipes/redirects) mutating command
|
||||
if $DRY_RUN; then log_dry "$*"; else "$@"; fi
|
||||
}
|
||||
|
||||
# used_vmids — every in-use guest id on this host. LXC (pct) and VMs (qm) SHARE the id space,
|
||||
# so both are consulted; headers (non-numeric first column) are filtered out.
|
||||
used_vmids() {
|
||||
{ pct list 2>/dev/null; qm list 2>/dev/null; } | awk '{print $1}' | grep -E '^[0-9]+$'
|
||||
}
|
||||
|
||||
# _vmid_in_use ID — true if ID is present in the pct+qm used-set (more complete than `pct status`,
|
||||
# which only knows LXC).
|
||||
_vmid_in_use() {
|
||||
local target="$1" used
|
||||
used=" $(used_vmids | tr '\n' ' ') "
|
||||
[[ "$used" == *" $target "* ]]
|
||||
}
|
||||
|
||||
# next_free_vmid BASE — the first id >= BASE not in the used-set, scanning upward.
|
||||
next_free_vmid() {
|
||||
local base="$1" used id
|
||||
used=" $(used_vmids | tr '\n' ' ') "
|
||||
id="$base"
|
||||
while [[ "$used" == *" $id "* ]]; do id=$((id + 1)); done
|
||||
echo "$id"
|
||||
}
|
||||
|
||||
# State helpers (robust JSON via python3).
|
||||
_state_has() {
|
||||
[[ -f "$STATE_FILE" ]] || return 1
|
||||
@@ -240,7 +266,7 @@ while [[ $# -gt 0 ]]; do
|
||||
--customer-id) CUSTOMER_ID="$2"; shift 2 ;;
|
||||
--mode) MODE="$2"; shift 2 ;;
|
||||
--hub-url) HUB_URL="$2"; shift 2 ;;
|
||||
--vmid) VMID="$2"; shift 2 ;;
|
||||
--vmid) VMID="$2"; VMID_EXPLICIT=true; shift 2 ;;
|
||||
--golden) GOLDEN_VOLID="$2"; shift 2 ;;
|
||||
--golden-vmid) GOLDEN_VMID="$2"; shift 2 ;;
|
||||
--archive-storage) ARCHIVE_STORAGE="$2"; shift 2 ;;
|
||||
@@ -304,7 +330,9 @@ read_passphrase() {
|
||||
[[ "$perm" == "600" || "$perm" == "400" ]] || log_warn "passphrase file $PASSPHRASE_FILE is mode $perm (want 600)"
|
||||
PASSPHRASE="$(< "$PASSPHRASE_FILE")"; PASSPHRASE="${PASSPHRASE%$'\n'}"
|
||||
else
|
||||
read -rsp "Retrieval passphrase for customer '${CUSTOMER_ID}': " PASSPHRASE; echo ""
|
||||
# Read from the terminal explicitly (not stdin), so the no-echo prompt works whether the
|
||||
# script is run from a file OR piped to bash (curl … | sudo bash) — where stdin is the pipe.
|
||||
read -rsp "Retrieval passphrase for customer '${CUSTOMER_ID}': " PASSPHRASE < /dev/tty; echo ""
|
||||
fi
|
||||
[[ -n "$PASSPHRASE" ]] || die "empty passphrase"
|
||||
}
|
||||
@@ -382,14 +410,29 @@ step_preflight() {
|
||||
log_info " golden: none local — will fetch + verify from Gitea in step 7/8"
|
||||
fi
|
||||
|
||||
# vmid guard (irrelevant when --skip-provision: we never touch a guest)
|
||||
# vmid guard (irrelevant when --skip-provision: we never touch a guest). "In use" is checked
|
||||
# against the pct+qm id-set (LXC and VMs share the space), not just `pct status`.
|
||||
if $SKIP_PROVISION; then
|
||||
log_info " --skip-provision: agent install/config only, no guest will be provisioned"
|
||||
elif pct status "$VMID" >/dev/null 2>&1; then
|
||||
if $FORCE; then
|
||||
elif _vmid_in_use "$VMID"; then
|
||||
if $VMID_EXPLICIT; then
|
||||
# Explicit --vmid stays deterministic: die unless --force (which over-provisions, destructive).
|
||||
if $FORCE; then
|
||||
log_warn " vmid $VMID already exists — --force given, it WILL be destroyed by provision"
|
||||
else
|
||||
die "vmid $VMID already exists. Refusing to clobber a live guest. Pass --force to provision over it."
|
||||
fi
|
||||
elif $FORCE; then
|
||||
# Default vmid + --force: honor the destructive over-provision without prompting.
|
||||
log_warn " vmid $VMID already exists — --force given, it WILL be destroyed by provision"
|
||||
else
|
||||
die "vmid $VMID already exists. Refusing to clobber a live guest. Pass --force to provision over it."
|
||||
# Default vmid in use, no --force: auto-pick the next free id and CONFIRM (never silent).
|
||||
local free_vmid; free_vmid=$(next_free_vmid "$VMID")
|
||||
log_info " vmid $VMID is in use; next free vmid is $free_vmid"
|
||||
local ans; read -rp "VMID $VMID is in use. Use next free VMID $free_vmid? [y/N] " ans < /dev/tty
|
||||
[[ "$ans" == "y" || "$ans" == "Y" ]] || die "no free vmid confirmed"
|
||||
VMID="$free_vmid"
|
||||
log_success " using auto-selected vmid $VMID"
|
||||
fi
|
||||
fi
|
||||
_state_mark preflight
|
||||
|
||||
Reference in New Issue
Block a user