diff --git a/scripts/CHANGELOG.md b/scripts/CHANGELOG.md index 823cbd0..bdecfc2 100644 --- a/scripts/CHANGELOG.md +++ b/scripts/CHANGELOG.md @@ -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 diff --git a/scripts/felhom-host-install.sh b/scripts/felhom-host-install.sh index 7e5a52b..0722078 100644 --- a/scripts/felhom-host-install.sh +++ b/scripts/felhom-host-install.sh @@ -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 -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 -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"