host-install v1.4.0: appliance CPU/RAM cap passthrough (--cores/--memory)

Optional --cores N / --memory M (MiB) passed through to the agent's
--selftest=provision as -cores/-memory ONLY when set (0/unset = golden default;
avoids unknown-flag death on an agent < v0.52.0). Pre-flight soft WARN when a cap
exceeds host nproc/MemTotal. Validated dry-run on felhom-pve. bash -n + shellcheck
clean. Deploy dependency: hub artifact manifest must serve agent >= v0.52.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-01 08:13:15 +02:00
parent 710afb066b
commit f7905b78b5
2 changed files with 58 additions and 5 deletions
+21
View File
@@ -1,5 +1,26 @@
# Felhom scripts — Changelog
## felhom-host-install.sh v1.4.0 — appliance CPU/RAM cap passthrough (`--cores` / `--memory`) (2026-07-01)
Colleague-safety batch #3 (host-install half; the mechanism is agent v0.52.0). Lets an operator cap the
provisioned guest so a trial appliance on a SHARED production Proxmox doesn't pressure the colleague's
existing guests.
- **`--cores N` / `--memory M` (MiB)** — optional; passed through to the agent's `--selftest=provision`
as `-cores`/`-memory`. `0`/unset = keep the golden's baked sizes (unchanged behaviour). New vars
`CPU_CORES`/`MEM_MIB`; `usage()` header gains an "Appliance cap (optional)" group.
- **Conditional passthrough** — `step_provision` builds a `cap_args` array and appends the flags to BOTH
the dry-run log and the real agent call **only when set**. An agent < v0.52.0 would reject an unknown
flag, so the flags are never sent unless the operator opts in (see the deploy dependency below).
- **Pre-flight sanity WARN (soft, provision only)** — if `--cores` > host `nproc` or `--memory` > host
`MemTotal`, `log_warn` "the cap won't protect other guests"; never `die` (the operator may know better).
- **Deploy dependency:** a fresh install using `--cores`/`--memory` needs the hub artifact manifest to
serve **agent ≥ v0.52.0**.
- **Validated dry-run on felhom-pve:** `--cores 2 --memory 4096 --dry-run` → provision command shows
`-cores 2 -memory 4096`; without the flags → neither present; `--cores 64 --memory 65536` → both WARN
lines (host 4 cores / ~15771 MiB). `bash -n` + `shellcheck` clean (0 new warnings; the 2 pre-existing
SC2015 in `step_verify` unchanged).
## felhom-host-install.sh v1.3.0 — `--uninstall` (clean revert) + pre-flight guards (2026-07-01)
Colleague-safety batch #1+#2. Adds a first-class, guarded **`--uninstall`** teardown so an operator can
+37 -5
View File
@@ -1,6 +1,6 @@
#!/bin/bash
#===============================================================================
# felhom-host-install.sh v1.3.0
# felhom-host-install.sh v1.4.0
# Day-0 host-bootstrap for a Felhom Proxmox host (operator-deploy model).
#
# Run by the operator on a FRESHLY-PVE-INSTALLED box (after a manual PVE install
@@ -45,6 +45,11 @@
# --rootfs-grow N grow OS rootfs by N GiB (default: auto-compute)
# --datavol-grow N grow Docker-data vol by N GiB (default: auto-compute)
# --sysdata-grow N grow user-data vol by N GiB (default: auto-compute)
#
# Appliance cap (optional — protect a SHARED host's other guests; needs agent >= v0.52.0):
# --cores N cap the guest to N CPU cores (0/unset = golden default)
# --memory M cap the guest RAM to M MiB (0/unset = golden default)
#
# --passphrase-file PATH read the retrieval passphrase from a 0600 file
# (default: secure no-echo prompt)
# --preserve-from PATH merge non-Day-0 sections (privileged/storage/backup/
@@ -85,7 +90,7 @@
set -euo pipefail
SCRIPT_VERSION="1.3.0"
SCRIPT_VERSION="1.4.0"
#-------------------------------------------------------------------------------
# Logging (mirrors felhom-controller/scripts/docker-setup.sh)
@@ -118,6 +123,8 @@ BRIDGE_ADDR=""
ROOTFS_GROW=""
DATAVOL_GROW=""
SYSDATA_GROW=""
CPU_CORES="" # --cores: optional appliance CPU-core cap (empty/unset = golden default)
MEM_MIB="" # --memory: optional appliance RAM cap in MiB (empty/unset = golden default)
PASSPHRASE_FILE=""
PRESERVE_FROM=""
PRESERVE_STATE_FROM="" # dir holding a prior local-api.{crt,key} + local-tokens.log to carry over (keeps the pin stable across a reinstall)
@@ -166,7 +173,7 @@ ART_GOLDEN_SHA=""
#-------------------------------------------------------------------------------
# Helpers
#-------------------------------------------------------------------------------
usage() { sed -n '2,75p' "$0" | sed 's/^# \{0,1\}//'; exit 0; }
usage() { sed -n '2,80p' "$0" | sed 's/^# \{0,1\}//'; exit 0; }
run() { # simple (no pipes/redirects) mutating command
if $DRY_RUN; then log_dry "$*"; else "$@"; fi
@@ -478,6 +485,8 @@ while [[ $# -gt 0 ]]; do
--rootfs-grow) ROOTFS_GROW="$2"; shift 2 ;;
--datavol-grow) DATAVOL_GROW="$2"; shift 2 ;;
--sysdata-grow) SYSDATA_GROW="$2"; shift 2 ;;
--cores) CPU_CORES="$2"; shift 2 ;;
--memory) MEM_MIB="$2"; shift 2 ;;
--passphrase-file) PASSPHRASE_FILE="$2"; shift 2 ;;
--preserve-from) PRESERVE_FROM="$2"; shift 2 ;;
--preserve-state-from) PRESERVE_STATE_FROM="$2"; shift 2 ;;
@@ -621,6 +630,23 @@ step_preflight() {
fi
fi
# Appliance-cap sanity (soft): a cap that EXCEEDS host resources won't protect other guests. WARN,
# never die — the operator may know better (e.g. capping below a future hardware upgrade).
if ! $SKIP_PROVISION; then
if [[ -n "$CPU_CORES" ]]; then
local host_cores; host_cores=$(nproc 2>/dev/null || echo 0)
if [[ "${host_cores:-0}" -gt 0 && "$CPU_CORES" -gt "$host_cores" ]]; then
log_warn " requested cap (${CPU_CORES} cores) exceeds host cores (${host_cores}); the cap won't protect other guests."
fi
fi
if [[ -n "$MEM_MIB" ]]; then
local host_mem_mib; host_mem_mib=$(awk '/^MemTotal:/{print int($2/1024)}' /proc/meminfo 2>/dev/null || echo 0)
if [[ "${host_mem_mib:-0}" -gt 0 && "$MEM_MIB" -gt "$host_mem_mib" ]]; then
log_warn " requested cap (${MEM_MIB} MiB) exceeds host RAM (~${host_mem_mib} MiB); the cap won't protect other guests."
fi
fi
fi
# archive-storage-exists guard (provision only — the golden lives there + the restore reads it).
if ! $SKIP_PROVISION; then
if pvesm status --storage "$ARCHIVE_STORAGE" >/dev/null 2>&1; then
@@ -1100,15 +1126,21 @@ step_provision() {
log_step "8/8 provision guest $VMID"
# NOTE: -hub-password is passed on argv (the agent's only input for it) — briefly
# visible in ps. Tracked as an Observation (candidate: env/stdin in the agent).
# Optional operator CPU/RAM cap — passed to the agent ONLY when set (an agent < v0.52.0 would
# reject the unknown flag and die; opt-in means no one hits that until they intentionally cap).
local -a cap_args=()
[[ -n "$CPU_CORES" ]] && cap_args+=(-cores "$CPU_CORES")
[[ -n "$MEM_MIB" ]] && cap_args+=(-memory "$MEM_MIB")
if $DRY_RUN; then
log_dry "felhom-agent --config $AGENT_CONFIG --selftest=provision -archive $GOLDEN_VOLID -vmid $VMID -customer-id $CUSTOMER_ID -hub-password <pass> -rootfs-grow $ROOTFS_GROW -datavol-grow $DATAVOL_GROW -sysdata-grow $SYSDATA_GROW"
log_dry "felhom-agent --config $AGENT_CONFIG --selftest=provision -archive $GOLDEN_VOLID -vmid $VMID -customer-id $CUSTOMER_ID -hub-password <pass> -rootfs-grow $ROOTFS_GROW -datavol-grow $DATAVOL_GROW -sysdata-grow $SYSDATA_GROW ${cap_args[*]}"
log_dry "record provisioned_vmid=$VMID in $STATE_FILE (for a later automatic --uninstall)"
_state_mark provision; return 0
fi
if ! felhom-agent --config "$AGENT_CONFIG" --selftest=provision \
-archive "$GOLDEN_VOLID" -vmid "$VMID" \
-customer-id "$CUSTOMER_ID" -hub-password "$PASSPHRASE" \
-rootfs-grow "$ROOTFS_GROW" -datavol-grow "$DATAVOL_GROW" -sysdata-grow "$SYSDATA_GROW"; then
-rootfs-grow "$ROOTFS_GROW" -datavol-grow "$DATAVOL_GROW" -sysdata-grow "$SYSDATA_GROW" \
"${cap_args[@]}"; then
die "provision FAILED — see the agent error above. Fix and re-run with --resume."
fi
log_success " provision completed"