Fix exit code bugs, doctor output, and labs parsing from live testing
Tested all new commands (--resources, --labs, --lab-down, --lab-up, --doctor, --status) against live Proxmox with 4 running IncusOS VMs. Fixes: - return → return 0 in 4 early-exit paths (pipefail + [[ ]] false propagated non-zero exit codes) - do_list_labs: broken ERE regex [^\ \]]+ → [^\ ]+ for config tag - do_list_labs: line-based Python output instead of delimiter-based tr (descriptions with spaces broke read parsing) - do_list_labs: unsafe echo|tr|sort pipeline → printf|sort - run_doctor: operations-center version → --version (shows 0.2.2) - run_doctor: ls glob pipeline crash under pipefail (|| true) CLAUDE.md: - Document env file (PROXMOX_TOKEN_SECRET) and source workflow - Add Sys.Audit to minimum API privileges (needed for --resources) - Correct role name to IncusOSDeployer - Add /nodes/pve ACL requirement for node-level endpoints Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f812e04746
commit
37d63c35a6
13
CLAUDE.md
13
CLAUDE.md
|
|
@ -20,6 +20,7 @@ incu-contrib/
|
|||
│ ├── incusos-proxmox # Declarative Proxmox VM deployment + lab lifecycle
|
||||
│ ├── lab-test # Guided lab validation (12 test phases)
|
||||
│ ├── proxmox.yaml # Proxmox connection config (gitignored, contains credentials)
|
||||
│ ├── ../env # PROXMOX_TOKEN_SECRET (gitignored); source with: source env
|
||||
│ ├── TESTING.md # Testing guide for incusos-proxmox and lab-test
|
||||
│ └── examples/ # Example seed + Proxmox YAML files
|
||||
└── notes/ # Research notes and reference material
|
||||
|
|
@ -88,15 +89,20 @@ incu-contrib/
|
|||
etc.) only define VM specs. Merge priority: `proxmox.yaml` base → lab config
|
||||
`proxmox:` overlay → CLI flags (`--host`, `--method`). Auto-discovery looks
|
||||
for `proxmox.yaml` in script directory then cwd; override with `--proxmox FILE`.
|
||||
- **API token secret**: stored in `env` file at the repo root (gitignored).
|
||||
Source it before running scripts: `source env` (exports `PROXMOX_TOKEN_SECRET`).
|
||||
- **Connection methods**: SSH (default, `ssh root@host qm ...`) or API
|
||||
(`curl -k https://host:8006/api2/json/...` with `PVEAPIToken` header).
|
||||
- **Minimum API privileges** for token-based access:
|
||||
- **Minimum API privileges** for token-based access (role: `IncusOSDeployer`):
|
||||
```
|
||||
VM.Allocate VM.Config.Disk VM.Config.CPU VM.Config.Memory
|
||||
VM.Config.Network VM.Config.CDROM VM.Config.Options VM.Config.HWType
|
||||
VM.PowerMgmt VM.Audit Datastore.AllocateSpace Datastore.AllocateTemplate
|
||||
Datastore.Audit SDN.Use
|
||||
Datastore.Audit SDN.Use Sys.Audit
|
||||
```
|
||||
`Sys.Audit` is needed for `--resources` (host RAM/CPU/uptime via
|
||||
`/nodes/<node>/status`). It requires a separate ACL on `/nodes/<node>`
|
||||
since the pool-scoped ACL doesn't cover node-level endpoints.
|
||||
- **Required VM settings** (getting any wrong causes IncusOS install failure):
|
||||
- `bios=ovmf`, `machine=q35` -- UEFI boot required
|
||||
- `efidisk0`: `pre-enrolled-keys=0` -- IncusOS enrolls its own Secure Boot keys
|
||||
|
|
@ -134,7 +140,8 @@ incu-contrib/
|
|||
```
|
||||
pveum pool add IncusLab --comment "IncusOS Lab VMs"
|
||||
pveum pool modify IncusLab --storage local-lvm,local
|
||||
pveum aclmod /pool/IncusLab -user automation@pve -role IncusOSDeploy
|
||||
pveum aclmod /pool/IncusLab -user automation@pve -role IncusOSDeployer
|
||||
pveum aclmod /nodes/pve -user automation@pve -role IncusOSDeployer -propagate 0
|
||||
```
|
||||
- **`--status` command**: `incusos-proxmox --status config.yaml` shows per-VM
|
||||
deployment status (Proxmox state, install status, IP, port 8443, incus
|
||||
|
|
|
|||
|
|
@ -586,7 +586,7 @@ run_doctor() {
|
|||
# operations-center CLI
|
||||
if command -v operations-center &>/dev/null; then
|
||||
local oc_ver
|
||||
oc_ver=$(operations-center version 2>/dev/null | head -1 || echo "installed")
|
||||
oc_ver=$(operations-center --version 2>/dev/null | head -1 || echo "installed")
|
||||
echo -e " operations-center CLI: ${GREEN}${oc_ver} ✓${RESET}"
|
||||
else
|
||||
echo -e " operations-center CLI: ${DIM}not installed (optional)${RESET}"
|
||||
|
|
@ -670,7 +670,7 @@ if versions:
|
|||
local cache_base="${XDG_CACHE_HOME:-${HOME}/.cache}/${SCRIPT_NAME}/iso-cache"
|
||||
if [[ -d "$cache_base" ]]; then
|
||||
local cached_iso
|
||||
cached_iso=$(ls -1 "${cache_base}"/IncusOS_*.iso 2>/dev/null | sort | tail -1)
|
||||
cached_iso=$(ls -1 "${cache_base}"/IncusOS_*.iso 2>/dev/null | sort | tail -1 || true)
|
||||
if [[ -n "$cached_iso" ]]; then
|
||||
local cached_name
|
||||
cached_name=$(basename "$cached_iso" .iso | sed 's/^IncusOS_//')
|
||||
|
|
@ -3511,8 +3511,13 @@ for v in data:
|
|||
local config_output
|
||||
config_output=$(pve_api GET "/nodes/${PVE_NODE}/qemu/${vmid}/config" 2>/dev/null) || continue
|
||||
|
||||
local desc name mem_mib disk_gib status
|
||||
read -r desc name mem_mib disk_gib < <(python3 -c "
|
||||
local name="" mem_mib="" disk_gib="" desc="" status
|
||||
{
|
||||
read -r name
|
||||
read -r mem_mib
|
||||
read -r disk_gib
|
||||
read -r desc
|
||||
} < <(python3 -c "
|
||||
import json
|
||||
data = json.loads('''${config_output}''').get('data', {})
|
||||
desc = data.get('description', '')
|
||||
|
|
@ -3526,8 +3531,11 @@ for part in scsi0.split(','):
|
|||
sz = part.strip().split('=')[1]
|
||||
if sz.endswith('G'):
|
||||
disk_g = int(sz[:-1])
|
||||
print(f'{desc}|||{name}|||{mem}|||{disk_g}')
|
||||
" 2>/dev/null | tr '|||' ' ' || true)
|
||||
print(name)
|
||||
print(mem)
|
||||
print(disk_g)
|
||||
print(desc)
|
||||
" 2>/dev/null || true)
|
||||
|
||||
# Check for managed marker
|
||||
if [[ "$desc" != *"$MANAGED_MARKER"* ]]; then
|
||||
|
|
@ -3537,7 +3545,7 @@ print(f'{desc}|||{name}|||{mem}|||{disk_g}')
|
|||
|
||||
# Extract config= tag
|
||||
local config_tag="unknown"
|
||||
if [[ "$desc" =~ config=([^\ \]]+) ]]; then
|
||||
if [[ "$desc" =~ config=([^\ ]+) ]]; then
|
||||
config_tag="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
|
||||
|
|
@ -3556,7 +3564,9 @@ print(f'{desc}|||{name}|||{mem}|||{disk_g}')
|
|||
return
|
||||
fi
|
||||
|
||||
for config in $(echo "${!lab_configs[@]}" | tr ' ' '\n' | sort); do
|
||||
local sorted_configs
|
||||
sorted_configs=$(printf '%s\n' "${!lab_configs[@]}" | sort)
|
||||
for config in $sorted_configs; do
|
||||
local vms="${lab_configs[$config]}"
|
||||
local ram_mib="${lab_ram[$config]}"
|
||||
local disk_gib="${lab_disk[$config]}"
|
||||
|
|
@ -3592,6 +3602,7 @@ print(f'{desc}|||{name}|||{mem}|||{disk_g}')
|
|||
done
|
||||
|
||||
[[ $unmanaged -gt 0 ]] && detail "${unmanaged} unmanaged VMs in pool"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -4374,7 +4385,7 @@ main() {
|
|||
|
||||
[[ "$RESOURCES" == true ]] && do_resources
|
||||
[[ "$LIST_LABS" == true ]] && do_list_labs
|
||||
return
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Handle cleanup-all without a config file (only needs proxmox.yaml)
|
||||
|
|
@ -4463,7 +4474,7 @@ main() {
|
|||
if [[ "$RESOURCES" == true ]] || [[ "$LIST_LABS" == true ]]; then
|
||||
[[ "$RESOURCES" == true ]] && do_resources
|
||||
[[ "$LIST_LABS" == true ]] && do_list_labs
|
||||
return
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Reconcile existing deployments (interactive menu on re-runs)
|
||||
|
|
|
|||
Loading…
Reference in New Issue